home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
TPUG - Toronto PET Users Group
/
TPUG Users Group CD
/
TPUG Users Group CD.iso
/
CRS
/
crs49.d81
/
hack10e.sfx
/
issue10e
Wrap
Text File
|
1990-02-12
|
97KB
|
2,581 lines
╘HE FILE INFORMATION IS THE SAME AS FOR OPENING AN UPLOAD FILE, EXCEPT THAT
THERE ARE MORE POSSIBLE RETURN CONDITIONS, AND ALL OF THE "META DATA" FIELDS
ARE ACTUALLY FILLED IN BY THE ╒NIX HOST (SINCE THIS INFORMATION IS ACTUALLY
CONVENIENTLY AVAILABLE VIA THE "STAT" SYSTEM CALL).
╔F THE SERVER REPLIES WITH A '0' "DATA TYPE" CODE, THEN THIS MEANS THAT THE
SERVER HAS NO MORE FILES TO OFFER FOR DOWNLOADING. ╘HE FILENAMES TO DOWNLOAD
ARE TAKEN ONE AT A TIME, FROM LEFT TO RIGHT, FROM THE COMMAND LINE THAT WAS
USED TO START THE SERVER. ╫HEN THE SERVER RUNS OUT, THEN THE DOWNLOADING
SESSION IS COMPLETE AND THE CLIENT DISCONNECTS (SINCE THE CLIENT UPLOADS
ITS FILES FIRST).
┴LTERNATIVELY, THE SERVER COULD REPLY WITH A 'E' CODE, WHICH MEANS THAT
IT COULD NOT OPEN THE NEXT FILENAME GIVEN ON ITS COMMAND LINE. ┴N ERROR
RETURN IS GENERATED SO THAT THE CLIENT CAN INFORM THE USER THAT THE FILE
COULD NOT BE DOWNLOADED. ╘HIS WILL NORMALLY RESULT FROM THE USER GIVING
A BAD FILENAME ON THE COMMAND LINE. ╘HE CLIENT WILL CONTINUE THE DOWNLOADING
PROCESS BY CLOSING THE DOWNLOAD CHANNEL (BELOW) ASKING FOR THE NEXT FILE BY
RE-OPENING THE DOWNLOAD CHANNEL. ╘HE DOWNLOAD CHANNEL NEEDS TO BE CLOSED
ON THIS CONDITION SINCE OTHERWISE THERE WOULD BE NO WAY OF DISTINGUISHING
RETRANSMISSIONS FROM NEW REQUESTS AT THE SERVER.
╞INALLY, THE SERVER CAN REPLY WITH A 'T' OR 'B' CODE ('D' FOR DIRECTORIES IS
NOT CURRENTLY IMPLEMENTED) INDICATING THAT THE FILE WAS CORRECTLY OPENED AND
IS EITHER TEXT OR BINARY (AS SPECIFIED ON THE SERVER'S COMMAND LINE). ╧F THE
META INFORMATION ABOUT THE FILE, ONLY THE FILENAME AND FILE SIZE ARE CURRENTLY
USED: THE FILE IS NAMED ACCORDING TO THE GIVEN NAME, TRANSLATED TO ╨┼╘╙├╔╔ AND
TRUNCATED TO 16 CHARACTERS, AND THE FILE SIZE IS REPORTED TO THE USER SO THAT
HE CAN MONITOR DOWNLOADING PROGRESS. ╔ AM NOT SURE WHAT TO DO YET ABOUT NAME
COLLISIONS ON THE ├OMMODORE END: EITHER ASK THE USER WHETHER TO OVERWRITE THE
FILE, AUTOMATICALLY OVERWRITE THE FILE ANYWAY, OR AUTOMATICALLY GIVE THE FILE
A SLIGHTLY DIFFERENT NAME AND DOWNLOAD NORMALLY. ╔ THINK THAT FOR THE TIME
BEING, ╔ WILL JUST OVERWRITE THE EXISTING FILE. ╘HIS WILL MEAN THAT YOU'LL
WANT TO BE EXTRA CAREFUL IN PUTTING THE FILENAMES ONTO THE CORRECT COMMAND
LINE (THE CLIENT'S OR THE SERVER'S), ALTHOUGH THERE WON'T BE A PROBLEM IF THE
FILE DOESN'T EXIST ON THE MACHINE WHOSE COMMAND LINE YOU PUT THE NAME ON.
╫HEN THE FILE HANDLING IS ALL SQUARED AWAY AND THE DOWNLOAD CHANNEL IS OPENED,
THE CLIENT THEN SUCKS PACKETS OUT OF THE FILE UNTIL THE END OF THE FILE IS
REACHED. ╘HE PACKETS ARE SUCKED OUT WITH THE FOLLOWING REQUEST:
╧╞╞ ╙╔┌ ─┼╙├
--- --- -----
0 1 CODE: ╥┼╤_─╧╫╬╠╧┴─_╨┴├╦┼╘ ('╙')
1 1 DOWNLOAD SEQUENCE NUMBER
2 4 MAXIMUM ACCEPTABLE DATA LENGTH: ╚/═/═/╠ WORD
6 - ╙╔┌┼
╘HE "DOWNLOAD SEQUENCE NUMBER" IS USED TO DISTINGUISH RETRANSMISSIONS FROM
REQUESTS FOR NEW PACKETS, AND THE CLIENT TELLS THE SERVER THE "MAXIMUM
ACCEPTABLE DATA LENGTH" FOR THE REPLY PACKET. ┴LTHOUGH THE MAX-PACKET
INFORMATION IS ACTUALLY STATIC DURING THE CONNECTION, ╔ INCLUDED IT HERE IN
EVERY "READ" REQUEST SINCE ╔ DIDN'T REALLY WANT THE SERVER TO KEEP THAT
PARTICULAR BIT OF "STATE" INTERNALLY.
╘HE SERVER REPLIES TO THE DOWNLOAD-PACKET REQUEST WITH THE FOLLOWING MESSAGE:
╧╞╞ ╙╔┌ ─┼╙├
--- --- -----
0 1 CODE: ┴├╦_─╧╫╬╠╧┴─_╨┴├╦┼╘ ('S')
1 1 DOWNLOAD SEQUENCE NUMBER
2 4 DATA LENGTH: ╚/═/═/╠ WORD, 0==┼╧╞
6 N DATA
6+N - ╙╔┌┼
╘HIS IS THE ONLY "LARGE" MESSAGE THAT THE SERVER CAN PRODUCE. ╔T INCLUDES THE
SEQUENCE NUMBER, THE NUMBER OF BYTES THAT ARE ACTUALLY INCLUDED, AND THE USER
DATA. ╘HE NUMBER OF DATA BYTES IN THE PACKET IS ALLOWED TO BE SMALLER THAN
THE NUMBER OF BYTES REQUESTED, BUT THIS IS NORMALLY ONLY THE CASE FOR THE LAST
PACKET OF THE FILE.
╘O INDICATE THAT THE END OF FILE HAS BEEN REACHED AND THAT NO MORE USER DATA
IS AVAILABLE, THE SERVER WILL RETURN A DOWNLOAD PACKET WITH ZERO BYTES OF USER
DATA IN IT. ╒PON RECEIVING THIS, THE CLIENT WILL CLOSE THE DOWNLOAD CHANNEL
WITH THE FOLLOWING MESSAGE:
╧╞╞ ╙╔┌ ─┼╙├
--- --- -----
0 1 CODE: ╥┼╤_─╧╫╬╠╧┴─_├╠╧╙┼ ('┼')
1 - ╙╔┌┼
┴ND THE SERVER WILL REPLY WITH:
╧╞╞ ╙╔┌ ─┼╙├
--- --- -----
0 1 CODE: ┴├╦_─╧╫╬╠╧┴─_├╠╧╙┼ ('E')
1 4 NUMBER OF FILE BYTES DOWNLOADED: ╚/═/═/╠ WORD
5 - ╙╔┌┼
╘HE "NUMBER OF FILE BYTES DOWNLOADED" FIELD IS REDUNDANT BUT INCLUDED FOR
ADDITIONAL ERROR CHECKING. ┴FTER CLOSING A FILE, THE CLIENT WILL THEN ASK
FOR THE NEXT FILE, OR WILL DISCONNECT IF THE LAST FILE TO DOWNLOAD WAS JUST
CLOSED.
4.4. ┼╥╥╧╥ ╚┴╬─╠╔╬╟
╫ITH ALL OF THE SERVER CALLS EXCEPT FOR DISCONNECTING (DISCUSSED EARLIER), THE
IS THE POSSIBILITY THAT EITHER THE REQUEST MESSAGE FROM THE CLIENT OR THE
REPLY MESSAGE FROM THE SERVER WILL BECOME GARBLED AND BE DROPPED BY THE
PACKET-DELIVERY LAYER OF THE SOFTWARE. ╘O RECOVER FROM THIS, IF THE CLIENT
DETECTS AN EXTENDED PERIOD OF INACTIVITY ON THE SERIAL LINE FOR RECEIVED DATA
(WHERE "EXTENDED PERIOD" IS DEFINED AS BEING "ABOUT FIVE SECONDS"), THEN THE
CLIENT WILL ASSUME THAT SOMETHING WENT WRONG AND IT WILL RETRANSMIT THE
REQUEST.
┴S POINTED OUT WAY ABOVE, THERE ARE TWO POSSIBLE REASONS FOR A RETRANSMISSION
BEING NEEDED: EITHER THE REQUEST PACKET WAS CORRUPTED AND DROPPED, OR THE
REPLY PACKET WAS CORRUPTED AND DROPPED. ╔N THE FORMAT CASE, THE REQUEST
WASN'T PROCESSED BY THE SERVER, BUT IN THE LATTER CASE, IT WAS. ╙INCE WE
DON'T WANT THE SERVER TO PERFORM AN FILE OPERATION TWICE (THIS IS REALLY
WHAT THE SIX FILE-TRANSFER CLIENT OPERATIONS REALLY BOIL DOWN TO FROM THE
SERVER'S PERSPECTIVE), THE SERVER MUST KEEP FOUR PIECES OF INTERNAL STATE:
THE LAST UPLOAD SEQUENCE NUMBER, THE LAST DOWNLOAD SEQUENCE NUMBER, WHETHER
THE UPLOAD FILE IS OPEN, AND WHETHER THE DOWNLOAD FILE IS OPEN.
╔F AN UPLOAD-OPEN REQUEST IS RECEIVED AND THE FILE TO BE UPLOADED IS NOT OPEN,
THE THE REQUEST MUST BE A NEW ONE AND THE SERVER PROCESSES IT AND SENDS BACK A
REPLY LIKE NORMAL. ╔F AN UPLOAD-OPEN REQUEST IS RECEIVE AND THE UPLOAD FILE
╔╙ CURRENTLY OPEN, THEN IT MUST BE THE CASE THAT THE CURRENT REQUEST IS A
RETRANSMISSION, SO ALL THEAT THE SERVER NEEDS TO DO IS TO GIVE A POSITIVE
REPLY WITHOUT PERFORMING ANY INTERNAL FILE OPERATIONS. ╘HE SAME HOLDS TRUE
FOR THE DOWNLOAD-OPEN CALL AND FOR BOTH OF THE CLOSE CALLS (EXCEPT THAT THE
OPERATION HAS ALREADY BEEN PROCESSED IF THE FILE IS ├╠╧╙┼─).
╞OR THE PACKET-UPLOAD AND PACKET-DOWNLOAD REQUESTS, SEQUENCE NUMBERS ARE USED
TO DETECT DUPLICATES. ┘OU WILL NOTE THAT THESE SEQUENCE NUMBERS ARE DISTINCT
FROM ONE ANOTHER, AND, IN FACT, THAT THE ENTIRE UPLOAD AND DOWNLOAD FILE-
TRANSFER CHANNELS ARE DISTINCT AND INDEPENDENT FROM EACH ANOTHER. ╘HIS IS TO
ALLOW FOR THE FUTURE POSSIBILITY OF SIMULTANEOUS FILE UPLOADING AND
DOWNLOADING. ╔N FACT, IF STREAM NUMBERS (FILE DESCRIPTORS) WERE ADDED TO THE
OPEN/READ/WRITE/CLOSE REQUESTS, THEN WE COULD HAVE US A FULL-BLOWN REMOTE-HOST
OVER-THE-PHONE INTERACTIVE FILE SERVER. ┬UT ANYWHO, SEQUENCE NUMBERS START
FROM 0X00 FOR THE FIRST PACKET TRANSFERRED AND INCREMENT MODULO 256 FROM
THERE.
╬OTE THAT FOR HIGH-SPEED DATA-COMPRESSION MODEMS (LIKE ╔ HAVE) THAT ALREADY
INCLUDE ERROR DETECTION AND RECOVERY AT A LEVEL HIDDEN FROM THE USER, THE ╞╪
PROTOCOL WILL WORK PARTICULARLY WELL: THERE WILL NEVER BE AN ERROR, NEVER BE A
TIMEOUT DELAY, AND NEVER BE A RETRANSMISSION. ┴ND, REALLY, THE ├╥├-32 ERROR
COMPUTATION AND CHECKING IS PRETTY MUCH A ZERO COST. ┬UT, IF SOMETHING DOES
GO WRONG, OUTSIDE OF THE MODEM-TO-MODEM CONNECTION, THE ╞╪ PROTOCOL IS RIGHT
THERE TO PICK UP THE PIECES AND CARRY ON.
6. ├╧╬├╠╒╙╔╧╬
┘OU'LL HAVE TO WAIT TO GET YOUR HANDS ON THE PROGRAM. ╘HE ╒NIX ╙ERVER
PROGRAM IS ALMOST 100% (EXCEPT FOR A FEW DESIGN CHANGES THAT ╔ MADE WHILE
WRITING THIS DOCUMENT), AND THE ┴├┼ PROGRAM IS IMPLEMENTED EXCEPT FOR
THE ERROR HANDLING AND TEXT CONVERSION. ┬OTH PROGRAMS WILL BE RELEASED
WITH THE NEXT RELEASE OF ┴├┼, WHICH WILL BE ╥EAL ╙OON ╬OW (╘═).
╚ERE IS MY PERFORMANCE TESTING SO FAR, USING MY ╒╙╥ ╙PORTSTER MODEM OVER A
14.4-KBPS PHONE CONNECTION, WITH A 38.4-KBPS LINK TO MY MODEM FROM MY ├128, TO
MY USUAL ╒NIX HOST:
╒SING ╞╪ TO/FROM THE ┴├┼ RAMDISK, ╥┼╒:
─OWNLOAD 156,260 BYTES OF ▐TEXT: TIME= 54.1 SEC, RATE=2888 CPS.
─OWNLOAD 151,267 BYTES OF TABULAR TEXT: TIME= 45.9 SEC, RATE=3296 CPS.
─OWNLOAD 141,299 BYTES OF ╩╨┼╟ IMAGE: TIME= 92.5 SEC, RATE=1528 CPS.
╒PLOAD 156,260 BYTES OF ▐TEXT: TIME= 57.4 SEC, RATE=2722 CPS.
╒PLOAD 151,267 BYTES OF TABULAR TEXT: TIME= 45.3 SEC, RATE=3339 CPS.
╒PLOAD 141,299 BYTES OF ╩╨┼╟ IMAGE: TIME= 95.0 SEC, RATE=1487 CPS.
╒SING ╞╪ TO/FROM MY ├═─ ╚ARD ─RIVE:
─OWNLOAD 156,260 BYTES OF ▐TEXT: TIME= 83.4 SEC, RATE=1874 CPS.
─OWNLOAD 151,267 BYTES OF TABULAR TEXT: TIME= 75.4 SEC, RATE=2006 CPS.
─OWNLOAD 141,299 BYTES OF ╩╨┼╟ IMAGE: TIME=118.2 SEC, RATE=1195 CPS.
╒PLOAD 156,260 BYTES OF ▐TEXT: TIME= 77.9 SEC, RATE=2006 CPS.
╒PLOAD 151,267 BYTES OF TABULAR TEXT: TIME= 66.2 SEC, RATE=2285 CPS.
╒PLOAD 141,299 BYTES OF ╩╨┼╟ IMAGE: TIME=114.2 SEC, RATE=1237 CPS.
╒SING ─ES╘ERM-128 V2.00 TO/FROM MY ├═─ ╚ARD ─RIVE, ┘-═ODEM:
─OWNLOAD 156,260 BYTES OF ▐TEXT: TIME=189.5 SEC, RATE= 824 CPS.
─OWNLOAD 151,267 BYTES OF TABULAR TEXT: TIME=180.4 SEC, RATE= 839 CPS.
─OWNLOAD 141,299 BYTES OF ╩╨┼╟ IMAGE: TIME=199.9 SEC, RATE= 707 CPS.
╒PLOAD 156,260 BYTES OF ▐TEXT: TIME=255.1 SEC, RATE= 611 CPS.
╒PLOAD 151,267 BYTES OF TABULAR TEXT: TIME=238.6 SEC, RATE= 634 CPS.
╒PLOAD 141,299 BYTES OF ╩╨┼╟ IMAGE: TIME=233.0 SEC, RATE= 606 CPS.
╒SING ╬OVA╘ERM-64 V9.5 TO MY ├═─ ╚ARD ─RIVE, ┌-═ODEM, ├64 MODE:
─OWNLOAD 156,260 BYTES OF ▐TEXT: TIME=245.8 SEC, RATE= 636 CPS.
─OWNLOAD 151,267 BYTES OF TABULAR TEXT: TIME=230.0 SEC, RATE= 658 CPS.
─OWNLOAD 141,299 BYTES OF ╩╨┼╟ IMAGE: TIME=262.6 SEC, RATE= 538 CPS.
(╘HERE IS NO ┌-═ODEM UPLOADING SUPPORT)
╙O THERE YOU HAVE IT: MY SIMPLE PROTOCOL BLOWS THE OTHERS AWAY. ╤┼─.
========================================================================
─┼╙╔╟╬ ┴╬─ ╔═╨╠┼═┼╬╘┴╘╔╧╬ ╧╞ ┴ '╥┼┴╠' ╧╨┼╥┴╘╔╬╟ ╙┘╙╘┼═ ╞╧╥ ╘╚┼ 128: ╨┴╥╘ ╔╔
BY ├RAIG ╙. ┬RUCE <CSBRUCE@CCNGA.UWATERLOO.CA>
0. ╨╥┼╞┴├┼
╘HERE HAS BEEN A SLIGHT CHANGE IN PLANS. ╔ ORIGINALLY INTENDED THIS ARTICLE
TO GIVE THE DESIGN OF A THEORETICAL DISTRIBUTED MULTITASKING MICROKERNEL
OPERATING SYSTEM FOR THE ├128. ╔ HAVE DECIDED TO GO A DIFFERENT ROUTE: TO
TAKE OUT THE DISTRIBUTED COMPONENT FOR NOW AND IMPLEMENT A REAL MULTITASKING
MICROKERNEL ╧╙ FOR A SINGLE MACHINE AND EXTEND THE SYSTEM TO BE DISTRIBUTED
LATER. ╘HE IMPLEMENTATION SO FAR IS, OF COURSE, ONLY IN THE PROTOTYPE STAGE
AND THE APPLICATION FOR IT IS ONLY A DEMO. ╨ART ╔╔╔ OF THIS SERIES WILL
EXTEND THIS DEMO SYSTEM INTO, PERHAPS, A USABLE DISTRIBUTED OPERATING SYSTEM.
1. ╔╬╘╥╧─╒├╘╔╧╬
╘HE PREVIOUS ARTICLE TALKED ABOUT THE GENERAL APPROACH TO BUILDING A
MULTITASKING MICROKERNEL ╧╙ FOR THE ├128. ╔T IS ASSUMED HERE THAT YOU HAVE
READ AND UNDERSTOOD THE PREVIOUS ARTICLE. ╘HIS ARTICLE GOES INTO THE GRUNGY
DETAILS OF IMPLEMENTING SUCH A BEAST. ╘HE PROTOTYPE KERNEL IMPLEMENTATION
PROVIDES SYSTEM CALLS TO CREATE AND "EXIT" USER PROCESSES, OBTAIN STATUS
INFORMATION, DELAY EXECUTION OF A PROCESS FOR A SPECIFIED PERIOD OF TIME,
AND TO PERFORM MESSAGE-PASSING INTERPROCESS COMMUNICATION.
├URRENTLY, THERE IS NO REAL MEMORY MANAGEMENT, NO REAL DEVICE DRIVERS, AND NO
PROCESS-RESOURCE RECLAMATION. ═ORE "INFRASTRUCTURE" FEATURES NEED TO BE ADDED
BEFORE A COMMAND-SHELL ENVIRONMENT OR ANY SUCH THING COULD BE SUPPORTED,
THOUGH NOT TOOOO MANY MORE; THE ├OMMODORE-╦ERNAL ╙ERVER IN THE DEMO SYSTEM
MAKES THE $╞╞─2 (├╚╥╧╒╘) ROUTINE OF THE ├OMMODORE ╦ERNAL AVAILABLE TO ALL
OTHER PROCESSES IN THE DEMO SYSTEM. ╔T COULD EASILY BE MODIFIED TO PROVIDE
ALL OF THE ├OMMODORE-╦ERNAL FEATURES TO THE OTHER PROCESSES, THEREBY GIVING
US A BASIC ╔/╧ SUB-SYSTEM.
╘HERE IS ALSO NO WAY TO DYNAMICALLY LOAD EXTERNAL PROGRAMS, SO THE TEST
PROGRAMS HAVE TO BE ASSEMBLED WITH THE KERNEL CODE. ╠OADING EXTERNAL PROGRAMS
IN THIS TYPE OF ENVIRONMENT HAS THE REQUIREMENT THAT THE PROGRAM WILL HAVE TO
BE RELOCATED UPON BEING LOADED TO AN ADDRESS THAT WOULD ONLY BE KNOWN AT LOAD
TIME. ╘HERE ARE TWO WAYS TO GO ON THIS: LOAD ALL PROGRAMS TO A FIXED ADDRESS
OR LOAD THEM TO DYNAMIC ADDRESSES. ╔F YOU LOAD THEM TO A FIXED ADDRESS, THEN
YOU CAN ONLY HAVE ONE PROCESSES LOADED AND CONCURRENTLY RUNNING ON EACH BANK
OF INTERNAL MEMORY OF THE ├128 AND THEN DEMAND-SWAP ALL OF THE OTHER PROCESSES
INTO AND OUT OF THESE (TWO) SLOTS, PRESUMABLY FROM ╥┼╒ MEMORY (ANY OTHER TYPE
WOULD BE MUCH TOO SLOW). ╔╚═╧, EVEN WITH ╥┼╒ MEMORY, THIS WOULD BE TOO SLOW,
ESPECIALLY FOR A MICROKERNEL ENVIRONMENT. ╙O, PROGRAMS WILL NEED TO BE
LOADED TO DYNAMIC ADDRESSES. ╞ORTUNATELY, ╔ HAVE A PROGRAM-RELOCATION
MECHANISM IN THE WORKS.
╘HE ENTIRE KERNEL AND THE DEMO PROGRAM FITS IN ├128 MEMORY IN THE SLOT BETWEEN
$1300 AND $1┬╞╞ OF ╥┴═0. ╘HE KERNEL USES STORAGE FROM $├00-$├╞╞ AND
$2000-$┬╞╞╞. ╘HE LATTER SECTION OF MEMORY IS USED UP IN 768-BYTE CHUNKS BY
EACH NEW PROCESS, SO THERE CAN BE A TOTAL OF 53 CONCURRENTLY EXECUTING USER
PROCESSES IN THE SYSTEM.
2. ╘┼╙╘ ╨╥╧╟╥┴═
╘HE TEST PROGRAM INCLUDES NO PROVISIONS FOR USER INTERACTION, SO IT MAY NOT
BE SOMETHING THAT YOU CAN IMPRESS YOUR FRIENDS WITH, BUT ╔ CAN ASSURE YOU
THAT ALL KINDS MULTITASKING STUFF IS GOING ON BEHIND THE SCENES TO MAKE
EVERYTHING HAPPEN.
╘HE DEMO TEST PROGRAM CREATES TEN PROCESSES. ╘HERE ARE FIVE "DELAY"
PROCESSES, TWO "BLABBERING" PROCESSES, A ├OMMODORE-╦ERNAL-╙ERVER PROCESS, ONE
╙╔─-BANGING PROCESS, AND THE ╬ULL PROCESS. (╬OTE THAT WHEN ╔ USE THE WORD
"KERNEL" ╔ AM REFERRING TO THE ╧╙ THAT ╔ HAVE WRITTEN, AND WHEN ╔ USE THE WORD
"╦ERNAL", ╔ AM REFERRING TO THE ├OMMODORE ╦ERNAL IN ╥╧═).
╘HE PURPOSE OF THE ├OMMODORE-╦ERNAL ╙ERVER IS TO RECEIVE REQUESTS FROM THE
WORKER PROCESSES TO CALL THE ├╚╥╧╒╘ ROUTINE TO PRINT A GIVEN LINE OF TEXT OUT
TO THE SCREEN. ╘HE ╦ERNAL SERVER IS THE ONLY PROCESSES THAT IS ALLOWED TO
CALL THE ├OMMODORE-╦ERNAL ROUTINES. ╙INCE IT CAN ONLY PROCESS ONE REQUEST
FROM A CLIENT PROCESS AT A TIME, CALLS TO THE ├OMMODORE ╦ERNAL ARE EFFECTIVELY
"SERIALIZED" (MADE TO HAPPEN ONE AFTER ANOTHER IN TIME), WHICH IS GOOD, SINCE
THINGS WOULD BLOW UP PRETTY BADLY IF TWO ACCESSES THE ├OMMODORE ╦ERNAL WERE TO
HAPPEN CONCURRENTLY.
╘HE "DELAY" PROCESSES, NUMBERED FROM 1 TO 5, EACH DELAY FOR A PERIOD OF ╬
SECONDS AND THEN REQUEST THE ╦ERNAL ╙ERVER TO PRINT A "╔'M ALIVE" MESSAGE TO
THE SCREEN. ╘HE NUMBER ╬ OF SECONDS TO DELAY IS THE NUMBER OF THE PROCESS.
┘OU SHOULD BE ABLE TO OBSERVE FROM WATCHING THE EXECUTION THAT EACH DELAY
PROCESS PRINTS A MESSAGE TO THE SCREEN WITH APPROXIMATELY THE CORRECT PERIOD
BETWEEN MESSAGES. ╬OTE THAT WHILE THESE PROCESSES ARE DELAYING, THEY DON'T
USE ANY ├╨╒ TIME, SO THE ├╨╒ TIME IS ALLOCATED TO OTHER PROCESSES. ╔F YOU
TRY HOLDING DOWN THE ├= KEY TO SLOW THE SCROLLING OR RUN THE SYSTEM IN ╙LOW
MODE, YOU WILL STILL NOTICE THAT THE DELAY PROCESSES GENERATE THEIR OUTPUT
AT APPROXIMATELY THE RIGHT TIME.
╘HE TWO "BLABBERING" PROCESSES, NAMED "BLABBER" AND "SPINNER", CONTINUALLY
SEND PRINT MESSAGES TO THE ├OMMODORE-╙ERVER PROCESS. ┘OU WILL OBSERVE THAT
THE MESSAGES FROM EACH COMES PRETTY MUCH PREFECTLY INTERLEAVED WITH EACH
OTHER, AND WITH THE OUTPUT OF THE DELAY PROCESS, BECAUSE OF THE INTERPROCESS
COMMUNICATION SCHEDULING POLICY: ╞╔╞╧ (FIRST-IN, FIRST-OUT).
╘HE ╙╔─-BANGING PROCESS RUNS CONTINUOUSLY IN THE BACKGROUND. ╔T INCREMENTS
THE 16-BIT FREQUENCY OF VOICE #1 FROM $0000 TO $FFFF AND THEN SUSPENDS ITS
EXECUTION FOR TWO SECONDS AND THEN REPEATS. ┴ SQUARE WAVE WITH EVEN PULSE
WIDTHS IS USED. ╫HEN THE ╙╔─ PROCESS DELAYS FOR TWO SECONDS, YOU WILL NOTICE
THAT THE PRINTING OPERATION OF THE OTHER PROCESSES SPEEDS UP A BIT. ╘HIS IS
BECAUSE THE ╙╔─ PROCESS IS A HEAVY ├╨╒ USER WHILE IT IS ACTIVE. ╘HE AMOUNT OF
SLOW-DOWN OF THE REST OF THE SYSTEM IS LIMITED A BIT BECAUSE THE ╙╔─ PROCESS
RUNS WITH A SLIGHTLY LOWER PRIORITY THAN THE OTHER PROCESSES IN THE SYSTEM
(WHICH MAKES THE SOUND INCREMENT SLIGHTLY SLOWER THAN IT OTHERWISE WOULD --
THERE'S ONLY SO MUCH ├╨╒ TO GO AROUND).
╘HE ╬ULL PROCESS IS NOT ACTUALLY NEEDED IN THIS EXERCISE, BUT IT WOULD
NORMALLY BE USED TO INSURE THAT THE SYSTEM ALWAYS HAD SOME PROCESS TO
SCHEDULE. ┴LL THAT IT DOES IS INCREMENT THE BINARY VALUE IN LOCATIONS
$0400-$0403 (ON THE 40-COLUMN SCREEN) WHENEVER IT IS ACTIVE. ╔T HAS THE
LOWEST PRIORITY IN THE SYSTEM AND NEVER GETS TO EXECUTE UNLESS ALL OF THE
OTHER PROCESSES ARE BLOCKED (I.E., ARE SUSPENDED FOR SOME REASON).
╫HEN YOU GET TIRED OF WATCHING THE DEMO, YOU CAN JUST HIT THE ╥┼╙╘╧╥┼ KEY.
╘HIS WILL CAUSE AN ╬═╔ WHICH WILL MAKE THE SYSTEM EXIT BACK TO ┬┴╙╔├. ╘HE
SYSTEM DOES NOT HAVE THE ABILITY TO HANDLE EXTERNAL EVENTS (LIKE KEY STROKES)
AT THIS TIME. ┴ COUPLE OF LOCATIONS ON THE 40-COLUMN SCREEN ARE USED FOR
STATUS INFORMATION, SO YOU WILL WANT TO RUN THE DEMO ON THE 80-COLUMN SCREEN.
3. ╨╥╧├┼╙╙ ├╧╬╘╥╧╠
┴ PROCESS IS A USER PROGRAM THAT IS IN AN ACTIVE STATE OF EXECUTION. ┴
PROCESS IS PERIODICALLY GIVEN A CERTAIN AMOUNT OF ├╨╒ TIME TO EXECUTE ITS
CODE, AND THEN ├╨╒ ATTENTION IS TAKEN AWAY FROM IT TO EXECUTE OTHER
PROCESSES. ╘HIS MAY SOUND LIKE YOU'RE SIMPLY MAKING ╬ PROCESSES RUN ╬ TIMES
SLOWER, AND THIS IS TRUE IN THE WORST CASE, BUT THE NORMAL CASE IS THAT MANY
PROCESSES IN THE SYSTEM WILL BE BLOCKED (FOR WHATEVER REASON) AND WILL NOT
REQUIRE ANY MORE ├╨╒ TIME UNTIL THEY WAKE UP AGAIN (FOR WHATEVER REASON).
╘HEREFORE, MULTITASKING IS A "WINNABLE" PROPOSITION.
╔N OUR SYSTEM, THE PROCESS THAT THE ├╨╒ IS CURRENTLY EXECUTING IS CHANGED
EVERY 1/60 OF A SECOND. ╘HIS IS A CONVENIENT "QUANTUM" PERIOD FOR A NUMBER OF
REASONS, INCLUDING THE FACT THAT, THANKS TO THE ══╒ OF THE ├128, "CONTEXT
SWITCHING" CAN BE EFFICIENTLY PERFORMED THIS QUICKLY.
3.1. ╨╥╧├┼╙╙-├╧╬╘╥╧╠ ├┴╠╠╙
╘HERE ARE SIX KERNEL CALLS THAT DEAL WITH PROCESS CONTROL:
├┴╠╠ ╬┴═┼ ╔╬╨╒╘ ┴╥╟╒═┼╬╘╙ ╥┼╘╒╥╬ ╓┴╠╒┼╙
----------- ------------------------------ -------------------------------
├REATE ( .┴┘=ADDRESS, .╪=PRIORITY ) .┴┘=NEW╨ID, .├╙=ERR(.┴=ERRCODE)
┼XIT ( .┴=CODE, .╪=$00 ) <NONE>
═Y╨ID ( ) .┴┘=PID
═Y╨ARENT╨ID ( ) .┴┘=PARENT╨ID
╙USPEND ( ) <NONE>
─ELAY ( .┴┘=JIFFIES ) <NONE>
3.1.1. ├╥┼┴╘┼
╘HE ├REATE() KERNEL CALL IS USED TO CREATE A NEW PROCESS. ╘HE FIRST INPUT
ARGUMENT IS THE CODE ADDRESS, AND IT IS PASSED IN THE .┴┘ REGISTER (.┴ IS
LOADED WITH THE LOW BYTE OF THE ADDRESS, AND THE .┘ REGISTER IS LOADED WITH
THE HIGH BYTE OF THE ADDRESS--.┴┘ FOR SHORT). ╘HE CODE MUST BE PRESENT IN
MEMORY AND BE READY TO BE EXECUTED, SINCE THERE IS NO FACILITY FOR LOADING
EXTERNAL PROGRAMS. ┴LSO, IF THE CODE IS NOT RE-ENTRANT, THEN THERE MUST BE NO
OTHER PROCESS ALREADY EXECUTING IT OR THINGS WILL LIKELY BLOW UP. ╥E-ENTRANT
CODE IS CODE THAT CAN BE EXECUTED BY MULTIPLE PROCESSES SIMULTANEOUSLY WITHOUT
CONFLICTS, ESSENTIALLY BECAUSE THERE ARE NO GLOBAL VARIABLES THAT COULD BE
BANGED ON BY MORE THAN ONE PROCESS AT A TIME.
╘HE PRIORITY ARGUMENT IS THE PRIORITY TO EXECUTE THE NEW PROCESS AT. ╓ALID
VALUES FOR THIS ARGUMENT ARE ON THE RANGE 0 TO 127. ╘HE SYSTEM KEEPS A LIST
AT ALL TIMES OF ALL THE PROCESSES THAT ARE READY TO EXECUTE, CALLED THE "READY
LIST". ╘HE WAY THAT THE SCHEDULING WORKS IS THAT A POINTER TO THE "ACTIVE"
PROCESS IS KEPT (THE ONE THAT IS CURRENTLY EXECUTING) AND THIS POINTER CYCLES
THROUGH THE READY LIST, TRYING TO ACTIVATE EACH PROCESS IN TURN, EVERY 1/60 OF
A SECOND. ╘HIS IS ROUNDROBIN SCHEDULING. ╘HE PRIORITY OF A PROCESS
DETERMINES THE NUMBER OF CYCLES THAT THE ACTIVE-PROCESS POINTER HAS TO TAKE
THROUGH THE LIST BEFORE THE PROCESS IS ACTIVATED. ╙O, IF A PROCESS HAS
PRIORITY 1, THEN IT WILL BE ACTIVATED ON EVERY ROUND; IF IT HAS A PRIORITY OF
2, THEN IT WILL BE ACTIVATED ON EVERY SECOND ROUND; AND IF IT HAS A PRIORITY
OF 86, THEN IT WILL BE ACTIVATED ONLY ON EVERY 86TH ROUND THROUGH THE READY
LIST. ╘HE HIGHER THE PRIORITY VALUE, THE SLOWER THE PROCESS EXECUTES. ╘HIS
POLICY GIVES A FAIR ALLOCATION OF THE ├╨╒ TO THE VARIOUS PROCESSES IN THE
SYSTEM.
╬ORMALLY, FOREGROUND PROCESSES (ONES THAT PERFORM ACTIONS RIGHT IN FRONT OF
THE USER'S FACE) SHOULD HAVE A PRIORITY OF 1, AND BACKGROUND PROCESSES SHOULD
HAVE A RELATIVELY LOWER PRIORITY. ┴ PRIORITY VALUE OF 0 FOR A PROCESS MEANS
THAT WHEN THE PROCESS IS ACTIVATED, IT WILL NOT BE DEACTIVATED AGAIN UNTIL IT
BLOCKS FOR SOME REASON. ╘HIS PRIORITY LEVEL SHOULD BE RESERVED FOR URGENT
COMPUTATIONS THAT BLOCK OFTEN, SINCE IT HAS THE POTENTIAL TO STARVE OUT THE
REST OF THE SYSTEM. ╘HE ╬ULL PROCESS EXECUTES AT A SPECIAL PRIORITY LEVEL
(255) THAT MAKES IT SO THAT IT WILL ONLY BE ACTIVATED IF THERE ARE NO OTHER
PROCESSES IN THE READY LIST.
╘HE ├REATE() CALL RETURNS WITH THE CARRY FLAG CLEAR AND THE PROCESS ID OF THE
NEWLY CREATED PROCESS IN THE .┴┘ REGISTER UPON SUCCESS, OR RETURNS WITH THE
CARRY FLAG SET AND AN ERROR CODE IN THE .┴ REGISTER UPON FAILURE. ╔ DO NOT
HAVE THE COMPLETE LIST OF ERROR CONDITIONS FIGURED OUT A THIS TIME, BUT ERRORS
WILL USUALLY HAPPEN ON A CALL LIKE THIS BECAUSE OF A LACK OF RESOURCES
(MEMORY) FOR THE KERNEL'S INTERNAL DATA STRUCTURES. ╒PON SUCCESSFUL RETURN,
THE CHILD PROCESS IS CREATED, MADE READY, AND MAY BE ACTIVATED BY THE SYSTEM
AT ANY TIME. ╘HE FIRST INSTRUCTION TO BE EXECUTED BY THE CHILD WILL BE AT THE
VALUE GIVEN FOR THE CODE ADDRESS.
╘HE NEWLY CREATED PROCESS WILL HAVE A CLEAR INDIVIDUAL STACK, EXCEPT FOR A
COUPLE OF BYTES ON THE VERY BOTTOM OF IT (HIGH ADDRESSES), AND A CLEAR
INDIVIDUAL ZEROPAGE. ╨ROCESSES ARE ALLOWED TO MAKE FULL USE OF EVERY LOCATION
IN THEIR ZEROPAGE, EXCEPT FOR THE ╔/╧ REGISTERS AT LOCATIONS 0 AND 1, AND FULL
USE OF THEIR STACK, EXCEPT THAT THEY MUST MAKE SURE THAT ABOUT A DOZEN BYTES
ARE AVAILABLE ON THE STACK AT ALL TIMES IN CASE AN INTERRUPT HAPPENS.
3.1.2. ┼╪╔╘
╘HE ┼XIT() KERNEL CALL IS USED TO REMOVE THE CURRENT PROCESS FROM THE SYSTEM.
╘HERE ARE TWO INPUT ARGUMENTS: THE .┴ REGISTER CONTAINS THE RETURN CODE THAT
WILL BE MADE AVAILABLE TO THE PARENT PROCESS IF IT IS INTERESTED (THOUGH NOT
IN THE CURRENT IMPLEMENTATION), AND A VALUE IN THE .╪ REGISTER, WHICH MUST
CURRENTLY BE $00. ╔ HAVEN'T FIGURED OUT EXACTLY HOW THE EXIT MECHANISM SHOULD
WORK YET AND IT CURRENTLY ONLY HAS A MINIMAL IMPLEMENTATION. ╘HE CALL DOES
MAKE THE KERNEL RECLAIM THE RESOURCES THAT WERE ALLOCATED TO THE PROCESS YET,
ALTHOUGH THIS FUNCTIONALITY WILL BE NEEDED IN ANY REAL OPERATING SYSTEM.
╘HERE IS NO RETURN VALUE FROM THE ┼XIT() CALL, BECAUSE THE CALL NEVER RETURNS.
╘HE SEMANTICS OF THE CALL IS THE THE PROCESS CALLING ┼XIT() WILL NEVER BE MADE
ACTIVE AGAIN. ┴LL PROCESSES SHOULD CALL ┼XIT() WHEN THEY ARE FINISHED
EXECUTING, OR THEY CAN ACHIEVE THE SAME RESULT BY EXECUTING AN ╥╘╙ INSTRUCTION
AT THE END OF THEIR MAIN ROUTINE. ╘HE KERNEL PUSHES THE ADDRESS OF AN ┼XIT()
STUB ROUTINE ONTO THE TOP OF THE STACK OF A USER PROCESS WHEN IT IS CREATED,
AND THE USER PROCESS WILL EXIT WITH A RETURN CODE OF $00 IN THIS CASE.
3.1.3. ═┘_╨╔─
╘HE ═Y╨ID() KERNEL CALL IS USED TO RETURN THE PROCESS IDENTIFIER OF THE
CURRENT PROCESS. ╘HIS CALL IS VERY SIMPLE, TAKES NO ARGUMENTS, AND EXECUTES
VERY QUICKLY. ╘HE RETURN VALUE IS THE PROCESS ID OF THE CALLING PROCESS AND
IS RETURNED IN THE .┴┘ REGISTER. ╘HIS CALL CANNOT FAIL, BECAUSE THE CURRENT
PROCESS MUST EXIST IN ORDER TO MAKE THE CALL IN THE FIRST PLACE, SO THERE IS
NO ERROR-RETURN CONDITION.
3.1.4. ═┘_╨┴╥┼╬╘_╨╔─
╘HE ═Y╨ARENT╨ID() KERNEL CALL IS USED TO RETURN THE PROCESS IDENTIFIER OF THE
PARENT PROCESS OF THE CURRENT PROCESS (I.E., THE PROCESS THAT CREATED THE
CURRENT PROCESS). ╘HIS CALL IS SIMPLE, TAKES NO ARGUMENTS, AND EXECUTES
QUICKLY, VERY MUCH LIKE THE ═Y╨ID() CALL. ╬O ERROR RETURNS ARE POSSIBLE, AND
THE PROCESS ID OF THE PARENT TO THE CURRENT PROCESS IS RETURNED IN THE .┴┘
REGISTER. ┬UT NOTE: IT IS NOT GUARANTEED THAT THE PARENT PROCESS WILL STILL
EXIST EITHER BEFORE OR AFTER THE CURRENT PROCESS MAKES THIS CALL; IT MAY HAVE
┼XIT()ED. ╔ MAY RE-THINK THIS SEMANTIC.
╘HIS CALL IS USEFUL FOR SETTING UP INTERPROCESS COMMUNICATION BETWEEN A CHILD
PROCESS AND ITS PARENT.
3.1.5. ╙╒╙╨┼╬─
╘HE ╙USPEND() KERNEL CALL IS USED TO SUSPEND THE EXECUTION OF THE CURRENTLY
EXECUTING PROCESS FOR AN INDEFINITE PERIOD OF TIME. ├URRENTLY, THIS PERIOD OF
TIME IS FOREVER, SINCE THERE IS NO CORRESPONDING "╥ESUME" SYSTEM CALL THAT
ANOTHER PROCESS CAN CALL IN ORDER TO WAKE UP THE PROCESS THAT SUSPENDED
ITSELF. ╘HE REASON THAT THIS CALL IS MADE AVAILABLE IS BECAUSE THE GUTS OF
WHAT IT DOES IS REQUIRED BY OTHER KERNEL OPERATIONS, AND THE COST OF MAKING
THIS CALL USER-ACCESSIBLE WAS THREE 6502 INSTRUCTIONS. ╘HIS CALL MAY BE
RETRACTED IN THE FUTURE, SINCE IT MAY CAUSE PROGRAMMERS TO DO BAD THINGS.
╘HE CALL TAKES NO ARGUMENTS, RETURNS NO VALUES, AND CURRENTLY, WILL NEVER
RETURN AT ALL, MUCH LIKE ┼XIT().
3.1.6. ─┼╠┴┘
╘HE ─ELAY() KERNEL CALL IS USED TO SUSPEND THE EXECUTION OF THE CURRENT
PROCESS FOR A USER-SPECIFIED PERIOD OF TIME. ╘HE DELAY PERIOD IS GIVEN IN
UNITS OF JIFFIES (1/60THS OF A SECOND). ╘HE UNSIGNED 16-BIT DELAY PERIOD IS
PASSED IN IN THE .┴┘ REGISTERS, GIVING A MAXIMUM POSSIBLE DELAY PERIOD OF
ABOUT 18 MINUTES. ╔F A USER PROCESS REQUESTS TO DELAY FOR A PERIOD OF ZERO
JIFFIES, ITS EXECUTION WILL NOT BE SUSPENDED AT ALL AND THE ─ELAY() PRIMITIVE
WILL RETURN IMMEDIATELY.
╙INCE THERE MAY BE OTHER PROCESSES IN THE SYSTEM DOING THINGS WHEN THE CURRENT
PROCESSES WAKES UP AFTER DOING A DELAY, YOU CAN THINK OF THE PROCESS DELAYING
FOR "AT LEAST" THE PERIOD OF TIME THAT YOU SPECIFY. ┴CTUALLY, TO MUDDY THINGS
EVEN MORE, YOUR PROCESS WILL ALWAYS GO TO SLEEP AT A MOMENT IN TIME THAT IS
INBETWEEN TWO TICKS OF THE JIFFY CLOCK, SO THE FIRST "JIFFY" THAT YOUR PROCESS
WAITS MAY ACTUALLY BE ANY PERIOD BETWEEN A COUPLE OF MICROSECONDS TO ALMOST A
FULL JIFFY, WITH A STATISTICAL AVERAGE OF HALF A JIFFY. ╘HIS IS AN ARTIFACT
OF ANY COARSE-TICK-BASED MECHANISM.
╘O MUDDY THINGS AGAIN, THE JIFFY TICKS, WHICH ARE CURRENTLY BASED ON ╓╔├
RASTER INTERRUPTS (ONE PER SCREEN UPDATE), MAY NOT BE PROCESSED IMMEDIATELY
WHEN THEY OCCUR, SINCE THE ╔╥╤ MAY BE DELAYED BY A SMALL PERIOD OF TIME IF
INTERRUPTS ARE DISABLED IN THE PROCESSOR STATUS REGISTER WHEN THE JIFFY TICK
HAPPENS. ┴ND FINALLY, YOU SHOULD NOTE THAT YOU WILL HAVE A DIFFICULT TIME
USING THIS CALL FOR TRUE "REAL TIME" PERIODIC OPERATIONS, LIKE PERFORMING SOME
SPECIFIC TASK PRECISELY EVERY TENTH OF A SECOND, SINCE THE CALL SPECIFIES A
PERIOD TO DELAY FOR, RATHER THAN A TIME TO WAKE UP AT. ╘HE ACTUAL PERIOD OF
YOUR PROCESS' ACTIVATIONS WILL BE DETERMINED BY THE WAITING TIME PLUS THE TIME
SKEW CAUSED BY THE PROCESSING THAT YOUR PROCESS DOES. ┴ ─ELAY╒NTIL() CALL
EASILY COULD BE IMPLEMENTED, IF ╔ FIGURE THAT IT WILL BE NEEDED FOR ANYTHING.
├URRENTLY, THE SCHEDULING POLICY IS TO MAKE PROCESSES ACTIVE IMMEDIATELY AFTER
THEY ARE AWAKENED, SO THIS MAKES THE ACTIVITIES OF OTHER PROCESSES LESS OF A
WORRY TO ACCURATE TIMING. ╒NIX DOES A SIMILAR THING BY GIVING A FRESHLY
AWAKENED PROCESS A TEMPORARILY HIGH PRIORITY, SINCE IT IS PROBABLY LIKELY THAT
THE PROCESS WILL DO SOME SMALL THINK AND THEN BLOCK AGAIN. ╘HIS POLICY
STATISTICALLY IMPROVES CONCURRENCY.
3.2. ╨╥╧├┼╙╙ ├╧╬╘╥╧╠ ┬╠╧├╦╙
┴ ╨ROCESS ├ONTROL ┬LOCK (╨├┬) IS THE DATA STRUCTURE THAT THE KERNEL KEEPS THE
INFORMATION THAT IT NEEDS TO KNOW ABOUT A PROCESS IN. ┴ ╨ROCESS IDENTIFIER
(PID) IS ACTUALLY THE ╥┴═0 ADDRESS OF THE PROCESS CONTROL BLOCK OF A PROCESS,
FOR CONVENIENCE, THOUGH THIS WILL HAVE TO CHANGE LATER. ╘HE FIELDS OF THE
PROCESS CONTROL BLOCK ARE SHOWN HERE, ORGANIZED INTO CLASSES:
╧╞╞ ╙╔┌ ├╠┴╙╙ ╠┴┬┼╠
--- --- ----- ------
0 2 QUEUE PCB╬EXT
2 2 QUEUE PCB╨REV
4 1 QUEUE PCB╔S╚EAD
5 1 QUEUE PCB╤├OUNT
6 1 CTXT PCB╙╨
7 1 CTXT PCB╙TACK╨AGE
8 1 CTXT PCB┌ERO╨AGE
9 1 CTXT PCB─506
10 1 SCHED PCB╨RIORITY
11 1 SCHED PCB├OUNTDOWN
12 2 SCHED PCB╫AKEUP╘IME
12 2 IPC PCB╙END═SG╨TR (OVERLAP)
12 2 IPC PCB╥ECV═SG╨TR (OVERLAP)
14 2 IPC/Q PCB╙END╤╚EAD
16 2 IPC/Q PCB╙END╤╘AIL
18 1 IPC/Q PCB╙END╤╞LAG
19 1 IPC/Q PCB╙END╤├OUNT
20 2 IPC PCB┬LOCKED╧N
22 2 IPC PCB╥ECEIVE╞ROM
24 2 PROC PCB╨ARENT
26 1 PROC PCB╙TATE
27 - - ╙╔┌┼
3.2.1. ╤╒┼╒┼-├╠┴╙╙ ╞╔┼╠─╙
╘HE FIRST FOUR FIELDS, OF THE CLASS "QUEUE" ARE USED FOR MAINTAINING A PROCESS
CONTROL BLOCK IN QUEUES WITH OTHER ╨├┬S. ╙OME GENERAL-PURPOSE QUEUE-HANDLING
ROUTINES HAVE BEEN WRITTEN TO MAKE QUEUE MANAGEMENT EASIER: ╤UEUE╔NIT(),
╤UEUE╔NSERT(), AND ╤UEUE╒NLINK(). ┼ACH QUEUE HAS A HEAD NODE, AND THE NODES
IN A DOUBLY LINKED CIRCULAR ORDER. ╘HIS MEANS THAT EACH NODE IN THE QUEUE HAS
A FORWARD ("PCB╬EXT") AND A BACKWARD ("PCB╨REV") POINTER AND THAT THE FIRST
NODE POINTS BACK TO THE HEAD AND THE LAST NODE IN A LIST POINTS FORWARD TO THE
HEAD. ╘HIS ORGANIZATION REMOVES ALL OF THE QUIRKS OF HANDLING NULL POINTERS
FROM THE CODE. ╒SING A DOUBLY LINKED ORGANIZATION MAKES IT EASY TO REMOVE AN
ARBITRARY NODE FROM THE MIDDLE OF A QUEUE.
┼ACH NODE ALSO HAS A "PCB╔S╚EAD" FIELD WHICH IS ALWAYS ╞ALSE (ZERO) AND A
"PCB╤├OUNT" FIELD WHICH IS ALWAYS ZERO. ╘HE HEAD IS THE SAME AS AN ENTRY IN
THE QUEUE, EXCEPT THAT ITS "PCB╔S╚EAD" FIELD IS SET TO ╘RUE ($FF) AND ITS
"PCB╤├OUNT" FIELD RECORDS THE NUMBER OF NODES THAT ARE IN THE QUEUE AT ANY
TIME. ╘HE "PCB╔S╚EAD" FIELD IS CHECKED WHEN SCANNING A LIST TO TELL IF YOU'VE
BUMPED BACK INTO THE HEAD NODE AGAIN, INDICATING THE END OF THE LIST. ╘HE
"PCB╤├OUNT" FIELD IS VERY CONVENIENT TO CHECK TO SEE WHETHER THE QUEUE IS
EMPTY OR NOT.
┴LL OF THE PROCESSES THAT ARE READY TO EXECUTE IN THE SYSTEM ARE KEPT IN THE
READY QUEUE. ╘HE ╨├┬ OF THE ╬ULL PROCESS ACTS AS THE HEAD FOR THIS QUEUE, AND
IS ALSO AN ACTIVE NODE IN THE QUEUE (A SMALL BUT HARMLESS KLUGE). ╘HE POINTER
TO THE ACTIVE PROCESS IS KEPT IN A KERNEL-ZERO-PAGE VARIABLE, AND SWEEPS
THROUGH THE CIRCULARLY LINKED READY-PROCESS LIST TO ACTIVATE NEW PROCESSES.
╘HE ACTIVE ╨├┬ IS NOT REMOVED FROM THE READY LIST WHILE IT IS ACTIVE.
3.2.2. ├╧╬╘┼╪╘-├╠┴╙╙ ╞╔┼╠─╙
╘HE NEXT FOUR FIELDS, OF THE CLASS "CTXT", STORE THE "CONTEXT" OF A PROCESS
THAT IS NOT STORED ON THE PROCESS' STACK WHEN IT IS NOT EXECUTING. ╘HESE
FIELDS INCLUDE SPACE FOR THE STACK POINTER, THE STACK PAGE, THE ZEROPAGE, AND
THE CONTENTS OF THE ══╒ REGISTER AT LOCATION $D506. ╘HE STACK POINTER IS
WHAT WAS IN THE ╙╨ REGISTER OF THE ├╨╒ WHEN THE PROCESS LAST PAUSED. ╘HE
STACK PAGE AND THE ZEROPAGE VALUES ARE THE VALUES IN ══╒ REGISTERS $D505 AND
$D507, RESPECTIVELY; THESE ARE THE PAGE NUMBERS OF THE PAGES IN ╥┴═0 MEMORY
THAT ARE ALLOCATED TO A PROCESS. ╘HESE PAGES CAN ONLY BE IN ╥┴═0 UNLESS
COMMON MEMORY IS DISABLED, FOR HARDWARE REASONS. ╔ MAY ALLOW THESE PAGES TO
BE IN EITHER ╥┴═0 OR ╥┴═1 IF THERE IS A NEED LATER. ╘HE $D506 REGISTER OF
THE ══╒ STORES THE MOST-SIGNIFICANT BITS OF THE ╥┴═ BANK THAT IS SELECTED IF
YOU HAVE EXPANDED INTERNAL MEMORY ON YOUR 128 (A LA ╘WIN├ITIES-128) AND THE
BANK SELECTION FOR ╥┼╒ (─═┴) OPERATIONS.
╘HE REST OF A PROCESS' CONTEXT IS STORED ON ITS STACK. ╚ERE IS WHAT A
PROCESS' STACK LOOKS LIKE JUST AFTER IT HAS BEEN CREATED:
┴──╥ ╙╨-╥┼╠ ─┼╙├╥╔╨╘╔╧╬
---- ------ ------------
$FF SP+09 EXITADDR-1.H
$FE SP+08 EXITADDR-1.L
$FD SP+07 PC.H
$FC SP+06 PC.L
$FB SP+05 STATUS REGISTER
$FA SP+04 .┴
$F9 SP+03 .╪
$F8 SP+02 .┘
$F7 SP+01 $FF00 SAVE
$F6 SP+00 -EMPTY-
╘HE "EXITADDR" IS THE ADDRESS OF THE ROUTINE IN THE KERNEL THAT WILL TERMINATE
A PROCESS IF IT EXECUTES AN ╥╘╙ FROM ITS MAIN ROUTINE. ╘HE ADDRESS IS IN THE
REGULAR LOW-HIGH ORDER (ALTHOUGH IT IS PUSHED ON HIGH-LOW SINCE THE STACK
GROWS DOWNWARD IN MEMORY) BUT THE VALUE PUSHED IS ACTUALLY ONE LESS THAN THE
ADDRESS OF THE ROUTINE, BECAUSE THIS IS WHAT ╩╙╥ PUSHES ONTO THE STACK AND
THIS IS WHAT ╥╘╙ EXPECTS TO FIND. ╘HE "PC" LOW AND HIGH FIELDS GIVE THE
ADDRESS OF THE NEXT INSTRUCTION TO BE EXECUTED BY THE PROCESS WHEN IT IS
ACTIVATED. ╫HEN THE PROCESS IS FIRST CREATED, THIS WILL BE THE ADDRESS OF THE
FIRST INSTRUCTION. ╘HE "PC" VALUE IS THE ACTUAL ADDRESS, NOT ONE BEFORE,
BECAUSE THIS IS WHAT A HARDWARE INTERRUPT PUSHES ONTO THE STACK, AND THIS IS
WHAT THE ╥╘╔ INSTRUCTION EXPECTS TO FIND.
╘HE "STATUS REGISTER", ".┴", ".╪", AND ".┘" FIELDS CONTAIN THE VALUES TO BE
LOADED INTO THE CORRESPONDING REGISTERS INSIDE OF THE ├╨╒ WHEN THE PROCESS IS
ACTIVATED. ╞OR A NEW PROCESS, THESE VALUES ARE ALL ZERO.
╘HE "$FF00 SAVE" IS THE VALUE TO BE LOADED INTO THE $FF00 "SHADOW" REGISTER OF
THE ══╒ WHEN THE PROCESS IS ACTIVATED. ╘HIS GIVES THE MEMORY CONTEXT THAT THE
PROCESS IS TO EXECUTE IN. ┴S THE KERNEL CURRENTLY ONLY WORKS WITH ONE BANK
CONFIGURATION (╥┴═0, ╬╧ ┬┴╙╔├ ╥╧═, ╔/╧ ENABLED, ╦┼╥╬┴╠ ╥╧═ ENABLED), THIS IS
THE VALUE PUT HERE WHEN A PROCESS IS CREATED.
╘HE FINAL FIELD IS "-EMPTY-" BECAUSE THE STACK POINTER IN THE 6502 POINTS TO
THE NEXT LOCATION IN STACK MEMORY THAT WILL BE USED. ┴LL OTHER VALUES IN THE
STACK ARE RELATIVE TO THIS. ╒PON STARTUP, THE STACK-POINTER FIELD OF THE
PROCESS CONTROL BLOCK WILL BE SET TO $F6, WHICH IS WHAT THE TABLE ABOVE
SHOWS.
╘HE STACK CONTENTS LOOK EXACTLY THE SAME AFTER A PROCESS HAS BEEN INTERRUPTED
BY A HARDWARE INTERRUPT, EXCEPT THAT THE STACK POINTER WILL LIKELY BE LOWER IN
THE STACK MEMORY, SO THE ABSOLUTE ADDRESSES IN STACK MEMORY IN THE ABOVE TABLE
NO LONGER APPLY AND THE "EXITADDR" BYTES ARE NOT PART OF THE INTERRUPT
CONTEXT. ╘HAT THINGS LOOK THE SAME IS NO COINCIDENCE; ON STARTUP, WE SET UP
THE STACK TO MAKE THINGS LOOK AS IF AN INTERRUPT HAD JUST OCCURRED, AND TO
START A PROCESS EXECUTING, WE EXECUTE THE CODE THAT RETURNS FROM AN INTERRUPT,
WHICH LOADS THE "CONTEXT" THAT IS ON THE STACK INTO THE PROCESS REGISTERS, AND
WE ARE READY TO ROCK.
╘HE ABOVE STACK ORGANIZATION IS EXACTLY THE SAME AS IT IS FOR PROCESSING
INTERRUPTS NORMALLY USING THE ├OMMODORE-╦ERNAL ENVIRONMENT ON THE 128, AND
THIS TOO IS NO COINCIDENCE BECAUSE THE CODE THAT SETS UP THE STACK LIKE THIS
UPON AN INTERRUPT IS BURNED INTO THE ╦ERNAL ╥╧═ AND THERE IS VERY LITTLE THAT
╔ CAN DO ABOUT IT. ╞ORTUNATELY, THE ORGANIZATION IS JUST FINE FOR OUR
PURPOSES.
3.2.3. ╙├╚┼─╒╠┼-├╠┴╙╙ ╞╔┼╠─╙
╘HE NEXT THREE FIELDS, OF CLASS "SCHED", ARE USED TO SCHEDULE THE PROCESS.
╘HE "PCB╨RIORITY" FIELD GIVES THE RELATIVE PRIORITY OF THE PROCESS ACCORDING
TO THE SCHEME ALREADY DISCUSSED. ╘HE "PCB├OUNTDOWN" FIELD IS USED TO KEEP
COUNT OF THE NUMBER OF REMAINING TIMES THAT THE PROCESS WILL HAVE TO BE
BYPASSED IN CYCLING THROUGH THE READY QUEUE BEFORE THE PROCESS WILL BE
ACTIVATED AGAIN. ╫HEN A PROCESS GIVES UP THE ├╨╒ UPON THE EXPIRATION OF ITS
TIME QUANTUM, THE "PCB├OUNTDOWN" FIELD IS LOADED WITH THE PCB╨RIORITY OF THE
PROCESS. ╫HEN THE "PCB├OUNTDOWN" VALUE REACHES ZERO, THE PROCESS IS SELECTED
FOR ACTIVATION.
╘HE "PCB╫AKEUP╘IME" FIELD IS USED WITH THE ─ELAY() KERNEL CALL TO INDICATE THE
ABSOLUTE SYSTEM TIME WHEN THE PROCESS SHOULD BE ACTIVATED AGAIN. ╘HE CURRENT
TIME IN THE SYSTEM IS KEPT IN A 16-BIT KERNEL VARIABLE, AND WRAPS AROUND EVERY
18.2 MINUTES. ╔F THE PROCESS IS NOT CURRENTLY TIME-DELAYED, THEN THE
╫AKEUP╘IME FIELD IS NOT USED (IN FACT, THE MEMORY MAY BE USED TO RECORD OTHER
STATUS INFORMATION).
3.2.4. ╔╨├-├╠┴╙╙ ╞╔┼╠─╙
╘HE EIGHT EIGHT FIELDS, OF CLASSES "IPC" AND "IPC/Q" ARE USED FOR INTERPROCESS
COMMUNICATION (MESSAGE PASSING). ╘HE FIRST TWO FIELDS, "PCB╙END═SG╨TR" AND
"PCB╥ECV═SG╨TR", ARE USED FOR STORING TEMPORARY VALUES FOR HANDLING MESSAGE
REQUESTS; THE FOUR "IPC/Q"-CLASS FIELDS ARE USED TO IMPLEMENT THE HEAD OF A
QUEUE OF PROCESSES THAT ARE WAITING TO COMMUNICATE WITH THE CURRENT PROCESS,
AND THE "PCB┬LOCKED╧N" FIELD INDICATES WHICH PROCESS THIS PROCESS IS WAITING
TO COMMUNICATE WITH, IF THE CURRENT PROCESS IS WAITING. ╘HE FIRST TWO FIELDS
ACTUALLY OVERLAP WITH EACH OTHER AND WITH THE "PCB╫AKEUP╘IME" FIELD DISCUSSED
EARLIER. ╘HIS IS OKAY SINCE NONE OF THE FIELDS WILL STORE ACTIVE STATUS
INFORMATION AT THE SAME TIME. ╘HE LAST FIELD, "PCB╥ECEIVE╞ROM" IS NOT USED AT
THIS TIME, BUT WILL BE USED IN THE FUTURE FOR AN PRIMITIVE TO RECEIVE A
MESSAGE ONLY FROM A SPECIFIC PROCESS. ╘HE INTERPROCESS COMMUNICATION IS
DISCUSSED IN MUCH GREATER DETAIL LATER.
3.2.5. ╨╥╧├-├╠┴╙╙ ╞╔┼╠─╙
╘HE FINAL TWO FIELDS, OF CLASS "PROC", STORE INFORMATION ABOUT THE STATUS OF
THE PROCESS. ╔ GUESS THE SAME CAN BE SAID OF ALL THE OTHER FIELDS. ┴NYWAY,
THE "PCB╨ARENT" FIELD INDICATES WHICH PROCESS IS THE PROCESS THAT CREATED THE
CURRENT PROCESS, AND THE RETURN VALUE FOR THE ═Y╨ARENT╨ID() KERNEL CALL IS
TAKEN FROM THIS FIELD.
╘HE "PCB╙TATE" FIELD GIVES THE CURRENT STATE OF THE PROCESS. ╚ERE ARE THE
DIFFERENT POSSIBLE PROCESS STATES:
╙╘┴╘┼ ╬┴═┼ ├╧─┼
--------------- ----
╙╘┴╘┼_╥┼┴─┘ $C0
╙╘┴╘┼_╙┼╬─ $C1
╙╘┴╘┼_╥┼├┼╔╓┼ $C2
╙╘┴╘┼_╥┼╨╠┘ $C3
╙╘┴╘┼_─┼╠┴┘ $C4
╙╘┴╘┼_╙╒╙╨┼╬─┼─ $C5
╘HE ╙╘┴╘┼_╥┼┴─┘ STATE MEANS THAT THE PROCESS IS IN THE READY QUEUE. ╘HE
╙╘┴╘┼_╙┼╬─, ╙╘┴╘┼_╥┼├┼╔╓┼, AND ╙╘┴╘┼_╥┼╨╠┘ STATES MEAN THAT A PROCESS IS
WAITING FOR SOME INTERPROCESS COMMUNICATION PRIMITIVE TO BE CALLED BY THE
PROCESS THAT IT IS COMMUNICATING WITH. ╘HE ╙╘┴╘┼_─┼╠┴┘ STATE MEANS THAT THE
PROCESS HAS CALLED THE ─ELAY() PRIMITIVE AND IS WAITING FOR SOME PERIOD OF
REAL TIME TO PASS BEFORE IT CAN BE ACTIVATED AGAIN. ╘HE ╙╘┴╘┼_╙╒╙╨┼╬─┼─ STATE
MEANS THAT A PROCESS HAS CALLED THE ╙USPEND() OR ┼XIT() PRIMITIVE AND WILL
NEVER BE MADE ACTIVE AGAIN (CURRENTLY, ╙USPEND() AND ┼XIT() MEAN THE SAME
THING).
╘HE STATE INFORMATION IS NEEDED FOR SOME OPERATIONS, AND WILL DEFINITELY BE
NEEDED BY A ╦ILL()-PROCESS OPERATION, TO FIND OUT WHAT STATE A PROCESS IS IN
SO THAT IT CAN BE REMOVED FROM ANY QUEUE OR WHATEVER IT IS IN, IN ORDER TO
OBLITERATE ALL INFORMATION ABOUT THE PROCESS FROM THE SYSTEM. ├URRENTLY,
THERE IS NO ╦ILL() CALL.
3.3. ╘┴╙╦ ├╥┼┴╘╔╧╬
╫HEN THE USER CALLS FOR THE CREATION OF A NEW PROCESS IN THE SYSTEM, LOTS OF
STUFF HAS TO HAPPEN. ╞IRST, MEMORY FOR THE PROCESS CONTROL BLOCK, ZERO PAGE,
AND STACK PAGE MUST BE ALLOCATED. ├URRENTLY, THIS ALLOCATION IS PERFORMED
VERY SIMPLY, BY KEEPING A PAGE POINTER AND INCREMENTING IT EVERY TIME A PAGE
IS ALLOCATED. ╘HE PROCESS CONTROL BLOCK ENDS UP GETTING ALLOCATED 256 BYTES
EVEN THOUGH IT ACTUALLY REQUIRES MUCH LESS THAN THAT. ╘HUS, EACH NEW PROCESS
CHEWS UP 768 BYTES OF MEMORY SPACE (PLUS CODE). ┴LSO, THERE IS CURRENTLY NO
MECHANISM FOR RECOVERING THE MEMORY ALLOCATED TO A PROCESS, WHICH IS OKAY
SINCE THE MECHANISM FOR ┼XIT()ING A PROCESS IS INCOMPLETE TOO.
╘HE ADDRESS THAT A ╨├┬ GETS ALLOCATED AT IS USED FOR THE PROCESS' ╨╔─ (PROCESS
IDENTIFIER). ╘HIS IS PARTICULARLY USEFUL SINCE THE REAL PURPOSE OF A ╨╔─ IS
TO CONVENIENTLY LOCATE THE PROCESS CONTROL BLOCK. ╘HIS WILL HAVE TO CHANGE
IN THE FUTURE, HOWEVER, SINCE THE ╨╔─S WILL ALSO HAVE TO LOCATE THE MACHINE
THAT A PROCESS IS ON.
╘HEN THE PROCESS CONTROL BLOCK MUST BE INITIALIZED. ╔T IS INITIALIZED AS
FOLLOWS:
╞╔┼╠─ ╙╔┌ ├╠┴╙╙ ╔╬╔╘╔┴╠ ╓┴╠╒┼
-------------- --- ----- --------------
PCB╬EXT 2 QUEUE 0
PCB╨REV 2 QUEUE 0
PCB╔S╚EAD 1 QUEUE 0
PCB╤├OUNT 1 QUEUE 0
PCB╙╨ 1 CTXT $F6
PCB╙TACK╨AGE 1 CTXT SET TO NEWLY ALLOCATED SPACE
PCB┌ERO╨AGE 1 CTXT SET TO NEWLY ALLOCATED SPACE
PCB─506 1 CTXT $04
PCB╨RIORITY 1 SCHED SET TO THE GIVEN ARGUMENT
PCB├OUNTDOWN 1 SCHED SET TO THE SAME VALUE AS "PCB╨RIORITY"
PCB╫AKEUP╘IME 2 SCHED 0 (OVERLOADED FIELD)
PCB╙END╤╚EAD 2 IPC/Q SET BY THE ╤UEUE╔NIT() FUNCTION FOR EMPTY QUEUE
PCB╙END╤╘AIL 2 IPC/Q SET BY THE ╤UEUE╔NIT() FUNCTION FOR EMPTY QUEUE
PCB╙END╤╞LAG 1 IPC/Q SET BY THE ╤UEUE╔NIT() FUNCTION FOR EMPTY QUEUE
PCB╙END╤├OUNT 1 IPC/Q SET BY THE ╤UEUE╔NIT() FUNCTION FOR EMPTY QUEUE
PCB┬LOCKED╧N 2 IPC 0
PCB╥ECEIVE╞ROM 2 IPC 0
PCB╨ARENT 2 PROC SET TO THE ID OF THE CREATOR PROCESS
PCB╙TATE 1 PROC ╙╘┴╘┼_╙╒╙╨┼╬─┼─
╘HE VALUES ON THE TOP OF THE STACK PAGE ARE ALSO INITIALIZED FOR THE NEW
PROCESS, AS FOLLOWS:
┴──╥ ╙╨-╥┼╠ ─┼╙├╥╔╨╘╔╧╬ ╔╬╔╘╔┴╠ ╓┴╠╒┼
---- ------ --------------- --------------
$FF SP+09 EXITADDR-1.H HIGH BYTE OF ┼XIT┴DDR-1: THE EXIT ROUTINE
$FE SP+08 EXITADDR-1.L LOW BYTE OF ┼XIT┴DDR-1: THE EXIT ROUTINE
$FD SP+07 PC.H HIGH BYTE OF THE CODE-EXECUTION ADDRESS
$FC SP+06 PC.L HIGH BYTE OF THE CODE-EXECUTION ADDRESS
$FB SP+05 STATUS REGISTER $00
$FA SP+04 .┴ $00
$F9 SP+03 .╪ $00
$F8 SP+02 .┘ $00
$F7 SP+01 $FF00 SAVE $0E (╦ERNAL ╥╧═, ╔/╧, REST ╥┴═0)
$F6 SP+00 -EMPTY- --
┴ND NOW, THE NEW PROCESS IS READY FOR ACTION. ╫E INSERT IT INTO THE READY
QUEUE IN THE NEXT POSITION AFTER THE CURRENT PROCESS, SET ITS STATE TO
╙╘┴╘┼_╥┼┴─┘ (ACTUALLY, BOTH OF THESE OPERATIONS ARE PERFORMED BY THE
═AKE╥EADY() FUNCTION, WHICH IS GENERALLY USEFUL AND IS CALLED FROM A NUMBER OF
PLACES) AND THEN WE EXIT BACK TO THE CALLING PROCESS, RETURNING THE PROCESS ID
OF THE CALLING PROCESS. ╔ SHOULD CHANGE THIS A LITTLE BIT IN THE FUTURE, TO
MAKE IT EXIT TO THE NEWLY CREATED CHILD PROCESS IF THE PRIORITY OF THE CHILD
PROCESS IS GREATER THAN THE PRIORITY OF THE PARENT.
3.4. ├╧╬╘┼╪╘ ╙╫╔╘├╚╔╬╟
├ONTEXT SWITCHING DESCRIBES THE PROCEDURE OF SWITCHING CONTROL OF THE
PROCESSOR FROM A USER PROCESS TO THE KERNEL AND THEN SWITCHING CONTROL BACK TO
A USER PROCESS. ╬ORMALLY, THERE IS ONLY ONE "STYLE" OF CONTEXT SWITCHING IN A
SYSTEM, BUT FOR A COUPLE OF DESIGN REASONS, ┬╧╙ ACTUALLY HAS THREE "STYLES" OF
CONTEXT SWITCHING: ╔╥╤ SWITCHING, ╩╙╥ SWITCHING, AND QUICK ╩╙╥ SWITCHING.
╔╥╤-STYLE SWITCHING IS THE ONE TYPE NORMALLY IMPLEMENTED IN OPERATING SYSTEMS
FOR OTHER ARCHITECTURES, SO IT WILL BE THE ONE THAT WE COVER FIRST.
╔╥╤-STYLE CONTEXT SWITCHING INVOLVES SAVING THE FULL CONTEXT OF A PROCESS ONTO
ITS STACK AND INTO ITS PROCESS CONTROL BLOCK, SWITCHING INTO THE KERNEL, DOING
WORK, SWITCHING BACK OUT OF THE KERNEL, AND RELOADING THE FULL CONTEXT OF A
USER PROCESS AND ACTIVATING TO IT. ┴LL OF THE WORK OF SAVING AND RESTORING
THE THE STACK PORTION OF A PROCESS' CONTEXT IS HANDLED BY THE ╥╧═ ROUTINES FOR
╔╥╤ (AND ╬═╔ AND ┬╥╦) HANDLING. ┴LL WE HAVE TO DO IS LOCATE THE CURRENT
PROCESS CONTROL BLOCK, SAVE THE ZERO-PAGE, STACK-PAGE, STACK-POINTER, AND
$D506 REGISTERS INTO THE ╨├┬, AND LOAD A $00 INTO THE ZERO-PAGE ══╒ REGISTER
TO SWITCH TO THE KERNEL'S ZEROPAGE (WHERE SOME OF THE KERNEL'S VARIABLES ARE
STORED). ╬OTE THAT THE INTERRUPT WILL BE EXECUTED USING THE USER PROCESS'
STACK; THEREFORE, ENOUGH SPACE SHOULD ALWAYS BE AVAILABLE ON USER STACKS TO
HANDLE THIS SYSTEM OVERHEAD.
╫HEN WE ARE DONE PROCESSING THE INTERRUPT, WE EXECUTE THE PRIORITY-MANAGEMENT
ALGORITHM THAT WAS DESCRIBED EARLIER TO SELECT THE NEXT PROCESS TO ACTIVATE,
AND THEN RESTORE THE ZERO-PAGE, STACK-PAGE, STACK-POINTER, AND $D506 REGISTERS
AND EXECUTE THE ╥╧═ STACK-HANDLING CODE FOR EXITING FROM AN INTERRUPT. ╬OTE
THAT THERE'S A CHANCE THAT WE MIGHT WELL BE EXITING TO A DIFFERENT USER
PROCESS FROM THE ONE THAT WAS ACTIVE WHEN THE INTERRUPT OCCURRED. ╘HERE
AREN'T MANY REGISTERS TO SAVE AND RESTORE, SO CONTEXT SWITCHING HAS A FAIRLY
LOW OVERHEAD, SO THERE IS NO PROBLEM IN DOING IT (AT LEAST) SIXTY TIMES A
SECOND.
╩╙╥-STYLE CONTEXT SWITCHING IS PRETTY MUCH THE SAME AS ╔╥╤-STYLE CONTEXT
SWITCHING, EXCEPT THAT THE STACK WILL NOT HAVE MOST OF THE PROCESSOR REGISTERS
ALREADY SAVED ON IT; IT WILL ONLY HAVE THE RETURN ADDRESS THAT PERFORMED THE
╩╙╥. ╔MMEDIATELY UPON ENTERING THE KERNEL, INTERRUPTS ARE DISABLED TO PREVENT
ALL SORTS OF BAD THINGS FROM HAPPENING. ╘HEN A FUNCTION IS CALLED,
┼NTER╦ERNEL(), WHICH WILL PULL THE RETURN ADDRESS OF THE PROCESS THAT CALLED
THE ╩╙╥ OFF THE STACK AND INCREMENT IT BY ONE (SINCE WE WILL BE EXITING BY
USING AN ╥╘╔ INSTRUCTION RATHER THAN AN ╥╘╙) AND SAVES THE OTHER PROCESSOR
REGISTERS ONTO THE STACK IN THE SAME WAY THAT THE INTERRUPT-HANDLING CODE IN
╥╧═ WOULD. ╘HEN WE SAVE THE FOUR ADDITIONAL REGISTERS INTO THE ╨├┬ AS BEFORE,
ACTIVATE THE KERNEL ZEROPAGE, AND WE ARE SWITCHED IN.
╘HIS STYLE OF CONTEXT SWITCHING IS USED FOR KERNEL CALLS THAT WILL CAUSE THE
CALLING PROCESS TO BLOCK (LIKE A NON-ZERO ─ELAY()). ╔T WOULD HAVE BEEN
POSSIBLE TO ORGANIZE THE KERNEL CALLS TO BE ENTERED BY EXECUTING A ┬╥╦
INSTRUCTION, WHICH WOULD HAVE CAUSED THE STACK TO BE ALREADY SET UP IN THE
SAME WAY AS WITH ╔╥╤ INTERRUPTS, BUT ╔ DECIDED AGAINST THIS FOR TWO REASONS:
EFFICIENCY, IT WOULD HAVE BEEN SLOWER TO DO THIS, AND DEBUGGING (SECURITY?),
SINCE ╔ ONLY WANT THE ┬╥╦ CONDITION TO SIGNAL A BUG IN THE CODE. ╘HE EXIT
FROM THIS TYPE OF CONTEXT SWITCH IS THE SAME AS FOR THE ╔╥╤ STYLE OF CONTEXT
SWITCH, SINCE THINGS ARE RIGGED TO END UP LOOKING THE SAME ON THE STACK. ╘HIS
IS A GOOD THING, SINCE THE ACTION THAT WILL CAUSE A ─ELAY()ED PROCESS TO BE
RE-ACTIVATED WILL, IN FACT, BE AN ╔╥╤ INTERRUPT.
╤UICK ╩╙╥-STYLE CONTEXT SWITCHING IS USED FOR KERNEL CALLS THAT WILL NOT BLOCK
OR CAUSE A NEW PROCESS TO BE ACTIVATED WHEN THEY FINISH, SUCH AS ═Y╨ID() OR
(CURRENTLY) ├REATE(). ╬O CONTEXT HAS TO BE SAVED SINCE THE FUNCTION WILL GET
IN AND OUT VERY QUICKLY; ALL WE HAVE TO DO IS SWITCH TO THE KERNEL'S ZEROPAGE
AND THEN SWITCH BACK TO THE USER'S ZEROPAGE BEFORE EXIT.
╘HERE'S ONE MORE NOTE TO MAKE ABOUT RETURN VALUES. ╞OR THE QUICK ╩╙╥-STYLE
CONTEXT SWITCH, THERE IS NO PROBLEM WITH RETURN VALUES, SINCE WE JUST HAVE TO
LOAD THEM INTO THE PROCESSOR REGISTERS AND EXIT. ╫ITH THE FULL ╩╙╥-STYLE
CONTEXT SWITCH, THE RETURN VALUES HAVE TO BE PUT ONTO THE USER STACK INTO THE
POSITIONS IN THE STACK MEMORY THE HOLD THE PROCESSOR REGISTER CONTENTS, SINCE
THESE VALUES WILL BE WHAT ARE RESTORED INTO THE PROCESSOR IMMEDIATELY UPON THE
RETURN TO THE USER PROCESS. ╘HERE ARE NO RETURN VALUES ASSOCIATED WITH THE
╔╥╤ STYLE OF CONTEXT SWITCHING (AND THERE'D BETTER NOT BE), SINCE AN INTERRUPT
CAN HAPPEN AT ANY POINT IN THE EXECUTION OF A USER PROCESS.
3.5. ─┼╠┴┘ ╨╥╔═╔╘╔╓┼
╘HERE ARE TWO COMPLEMENTARY HALVES TO THE IMPLEMENTION OF THE ─ELAY()
PRIMITIVE: THE HALF THAT IS CALLED BY THE USER AND CAUSES A PROCESS TO GO TO
SLEEP, AND THE HALF THAT WAKES UP A SLEEPING PROCESS AT THE CORRECT TIME.
╘HIS LATTER HALF IS EXECUTED BY THE 60-╚Z SYSTEM INTERRUPT.
3.5.1. ╒╙┼╥ ╚┴╠╞ ╧╞ ╘╚┼ ─┼╠┴┘ ╨╥╔═╔╘╔╓┼
╘HE FIRST THING THAT THE USER HALF OF THE ─ELAY() PRIMITIVE DOES IS CHECK TO
SEE IF THE DELAY PERIOD IS ZERO JIFFIES. ╔F IT IS, THEN THE PRIMITIVE RETURNS
IMMEDIATELY TO THE CALLING PROCESS WITHOUT RESCHEDULING (WITHOUT SKIPPING TO
THE NEXT READY PROCESS IN LINE). ╔ MAY CHANGE THIS SEMANTIC, BECAUSE IT IS
OFTEN USEFUL TO HAVE A PRIMITIVE THAT YEILDS PROCESS EXECUTION TO THE NEXT
READY PROCESS WITHOUT ACTUALLY BLOCKING THE CURRENT PROCESS.
╔F THE DELAY PERIOD IS LONGER THAN ZERO JIFFIES, THEN THE CURRENT PROCESS IS
SUSPENDED AND REMOVED FROM THE READY QUEUE, AND THE ABSOLUTE TIME THAT THE
PROCESS IS TO BE REAWAKENED IS CALCULATED AND PUT INTO THE "PCB╫AKEUP╘IME"
FIELD OF THE ╨├┬ FOR THE CURRENT PROCESS. ╘HE ABSOLUTE WAKEUP TIME IS
CALCULATED, OF COURSE, BY ADDING THE NUMBER OF JIFFIES TO DELAY TO THE CURRENT
ABSOLUTE TIME, WHICH IS MAINTAINED BY THE SYSTEM AND INCREMENTED ON EVERY
(60 ╚Z) SYSTEM INTERRUPT.
╘HEN THE CURRENT PROCESS CONTROL BLOCK IS INSERTED INTO THE DELAY QUEUE AT THE
CORRECT POSITION. ╘HE DELAY QUEUE IS A QUEUE (IMPLEMENTED IN THE STANDARD
WAY) OF PROCESS CONTROL BLOCKS FOR PROCESSES WHICH ARE ASLEEP, ORDERED BY THE
ABSOLUTE WAKEUP TIME OF EACH PROCESS SUCH THAT THE PROCESS THAT WILL BE
AWAKENED AT THE NEAREST TIME IN THE FUTURE IS AT THE HEAD OF THE LIST AND THAT
THE PROCESS WHICH WILL BE AWAKENED AT THE FARTHEST POINT IN THE FUTURE IS AT
THE TAIL. ╘HE FOLLOWING DIAGRAM GIVES AN EXAMPLE:
├URRENT╘IME = 2016
+---------+ +---------+ +---------+ +---------+
--->▄ ╨ROC ┴ ▄----->▄ ╨ROC ┬ ▄----->▄ ╨ROC ├ ▄----->▄ ╨ROC ─ ▄
▄ WAKEUP: ▄ ▄ WAKEUP: ▄ ▄ WAKEUP: ▄ ▄ WAKEUP: ▄
<---▄ @ 2345 ▄<-----▄ @ 2765 ▄<-----▄ @ 54999 ▄<-----▄ @ 441 ▄
+---------+ +---------+ +---------+ +---------+
(CT+5.5SEC) (CT+12.5SEC) (CT+14.7MIN) (CT+17.8MIN)
╘HERE IS A RUB HERE: ONLY 16 BITS ARE USED FOR STORING TIMES, WHICH EQUALS
ABOUT 18.2 MINUTES, SO WE HAVE TO WORRY ABOUT TIME QUANTITIES OVERFLOWING AND
WRAPPING AROUND. ╞OR EXAMPLE, IF THE CURRENT TIME IS 48232 AND A PROCESS
WANTS TO SLEEP FOR 18000 JIFFIES (5 MINUTES), THEN ITS WAKEUP TIME WOULD BE AT
696 JIFFIES, ACCOUNTING FOR THE 16-BIT WRAPAROUND, WHICH IS A LOWER NUMERICAL
VALUE THAN THE CURRENT TIME, OR THAN THE WAKEUP TIME OF ANY OTHER PROCESS THAT
WILL WAKE UP BEFORE THE CURRENT-TIME WRAPAROUND. ╔N FACT, ALL TIMERS HAVE
THIS WRAPAROUND PROBLEM (ALTHOUGH WITH 64-BIT TIMES, WRAPAROUND PERIODS WOULD
BE EXPRESSED IN MILLIONS OF MILLENNIA RATHER THAN IN MINUTES). ╙IXTEEN BITS
IS A GOOD NUMBER OF BITS TO USE, HOWEVER, BECAUSE THAT IS THE MAXIMUM DELAY
PERIOD (2^16-1).
╫HEN WE INSERT A NEW PROCESS INTO THE DELAY QUEUE, WE SCAN THE DELAY QUEUE
FROM THE HEAD AND CONTINUE UNTIL WE FIND A RECORD THAT HAS A TIME THAT IS
HIGHER THAN OR EQUAL TO THE WAKEUP TIME OF THE NEW PROCESS (OR WE HIT THE END
OF THE QUEUE). ╘HEN, WE INSERT THE NEW PROCESS IMMEDIATELY BEFORE THIS
POINT. ╘O HANDLE THE WRAPAROUND PROBLEM, ALL COMPARISONS OF WAKEUP TIMES ARE
DONE USING 17 BITS (WELL, REALLY 24 BITS). ╞OR EACH VALUE IN THE COMPARISON,
WE ADD 65536 TO IT (SET ITS 17TH BIT) IF THE VALUE IS LESS THAN THE CURRENT
TIME. ╫E DON'T HAVE TO WORRY ABOUT THE CURRENT TIME CHANGING WHILE WE ARE
DOING THIS, BECAUSE INTERRUPTS WILL BE DISABLED FOR THE ENTIRE TIME THAT
WE ARE EXECUTING THE SYSTEM CALL, AS PER USUAL. ╘HINGS COULD GO HORRIBLY
WRONG ANYWAY IF INTERRUPTS WERE NOT DISABLED.
╧KAY, SO NOW OUR DELAYING PROCESS IS REMOVED FROM THE READY QUEUE, ITS
COMPLETE CONTEXT IS SAVED, AND IT IS PUT INTO THE DELAY QUEUE AT THE RIGHT
SPOT. ╙O, SET THE ACTIVE PROCESS POINTER TO THE NEXT READY PROCESS IN THE
SYSTEM AND FINISH BY ACTIVATING THE NEXT READY PROCESS.
3.5.2. ╙┘╙╘┼═ ╚┴╠╞ ╧╞ ╘╚┼ ─┼╠┴┘ ╨╥╔═╔╘╔╓┼
─URING EACH 60-╚Z SYSTEM INTERRUPT, THE CURRENT TIME (JIFFY COUNTER) IS
INCREMENTED BY ONE. ╬OTE THAT SINCE THIS TIMER IS ONLY 16 BITS WIDE, IT IS
NOT SUITABLE FOR KEEPING TRACK OF THE CURRENT TIME OF DAY; FOR THIS PURPOSE,
THE ╘╧─ CLOCKS IN THE ├╔┴ CHIPS SHOULD (AND WILL) BE USED. ╘HE JIFFY COUNTER
MAY ALSO BE INACCURATE IF INTERRUPTS ARE DISABLED FOR A LONG PERIOD OF TIME,
SUCH AS THEY ARE DURING SOME ├OMMODORE-╦ERNAL ╔/╧ OPERATIONS.
┴FTER INCREMENTING THE TIME, THE KERNEL CHECKS TO SEE IF ANY ─ELAY()ED
PROCESSES NEED TO BE WOKEN UP. ╔F THERE ARE NO PROCESSES IN THE DELAY QUEUE,
THEN THIS IS A QUICK CHECK. ╔F THERE ARE ANY PROCESSES IN THE QUEUE, THEN IF
THE WAKEUP TIME OF THE HEAD PROCESS IS EQUAL TO THE CURRENT TIME, THEN THAT
PROCESS IS WOKEN UP AND THIS CHECK IS PERFORMED REPEATEDLY UNTIL THE CONDITION
FAILS, SINCE THERE MAY BE MULTIPLE PROCESSES THAT WANT TO BE WOKEN UP AT THE
SAME JIFFY OF ABSOLUTE TIME. ╬OTE THAT BECAUSE OF THE SCHEDULING FOR A
FRESHLY UNBLOCKED PROCESS, THE PROCESS THAT ─ELAY()ED FIRST WILL BE THE
FIRST ONE ACTIVATED AFTER IT IS WOKEN UP, IF THERE ARE MULTIPLE PROCESSES
WOKEN UP AT THE START OF THE SAME JIFFY.
3.6. ╙┘╙╘┼═ ┬╧╧╘╙╘╥┴╨╨╔╬╟
╧PERATING SYSTEMS ALWAYS HAVE A BOOTSTRAPPING PROBLEM, BECAUSE YOU ALWAYS NEED
TO USE THE SERVICES OF THE OPERATING SYSTEM IN ORDER TO START IT UP, BUT, OF
COURSE, IT'S NOT STARTED UP YET, CHICKEN AND EGG, CATCH-22. ╙O, WHAT USUALLY
ENDS UP HAPPENING IS THAT YOU JUST "FAKE IT", START FROM SOMEWHERE, GET THE
BALL ROLLING, AND SNOWBALL UP TO A FULLY RUNNING SYSTEM.
╘HE FIRST THING THAT THE KERNEL DOES IS CHANGE ALL OF THE INTERRUPT VECTORS
(╔╥╤, ╬═╔, AND ┬╥╦) TO MY CUSTOM ROUTINES. ╔ NEED TO COVER ALL OF THE
INTERRUPTS, SINCE ╔ CHAVE THE ZERO PAGE DURING THE EXECUTION OF THE SYSTEM,
AND IF A ┬╥╦ OR ╬═╔ WERE TO HAPPEN AND BE SERVICED BY THE ├OMMODORE-╦ERNAL ╥╧═
ROUTINES, ALL HELL WOULD BREAK LOOSE. ├URRENTLY, THE ╬═╔ AND ┬╥╦ ROUTINES
JUST CLEAN THINGS UP AND RETURN TO ┬┴╙╔├.
╘HEN WE INITIALIZE THE KERNEL VARIABLES, INCLUDING THE DELAY QUEUE AND THE
JIFFY COUNTER.
┴ND THEN WE FAKE THE CREATION OF THE ╬ULL PROCESS. ╞OR THE PURPOSES OF
BOOTSTRAPPING, THE ╬ULL PROCESS DOUBLES AS THE "┬OOT" PROCESS. ╔TS PROCESS
CONTROL BLOCK IS NOT ALLOCATED IN THE NORMAL WAY, EITHER; IT IS AT A FIXED
LOCATION, AND ITS ╨├┬ DOUBLES AS THE HEAD OF THE PROCESS LIST. ┴ KLUGE HERE
AND A HACK THERE AND THE ╬ULL PROCESS IS INITIALIZED AND "JOINED IN
PROGRESS". ╘HEN, THE ╬ULL PROCESS CREATES THE ╔NIT PROCESS, USING A STANDARD
CALL TO THE ├REATE() PRIMITIVE, AND THEN THE ╬ULL PROCESS GOES INTO AN ENDLESS
LOOP OF INCREMENTING THE 32-BIT VALUE AT ADDRESSES $400-$403, THE FIRST FOUR
LOCATIONS OF THE 40-COLUMN SCREEN MEMORY. ╔T DOESN'T MATTER WHETHER YOU RUN
┬╧╙ WITH THE CLOCK IN ╞AST OR ╙LOW MODE, EXCEPT IN TERMS OF PERFORMANCE.
╔T IS THE RESPONSIBILITY OF THE ╔NIT PROCESS TO START UP ALL OF THE USER
PROCESSES IN THE USER APPLICATION AFTER ╔NIT STARTS RUNNING. ╔N THE CURRENT
IMPLEMENTATION, ╔NIT STARTS UP ALL OF THE OTHER PROCESSES IN THE TEST
APPLICATION AND THEN BECOMES THE ├OMMODORE-╦ERNAL ╙ERVER, WHICH IS A
CONVENIENT ORGANIZATION, SINCE ALL OF THE OTHER PROCESSES CAN FIND OUT THE PID
OF THE ╦ERNAL ╙ERVER MERELY BY CALLING ═Y╨ARENT╨ID().
4. ╔╬╘┼╥╨╥╧├┼╙╙ ├╧══╒╬╔├┴╘╔╧╬
╔N THIS SYSTEM, PROCESSES ARE NOT STRICTLY INDEPENDENT AND COMPETITIVE; MANY
MUST COOPERATE AND COMUNICATE TO GET WORK DONE. ╘O FACILITIATE THIS
INTERPROCESS COMMUNICATION (╔╨├), A PARTICULAR PARADIGM WAS CHOSEN: THE ╥EMOTE
╨ROCEDURE ├ALL (╥╨├) PARADIGM. ╥╨├ IS A MESSAGE-PASSING SCHEME THAT IS USED
WITH THE MUCH-HYPED ├LIENT/╙ERVER SYSTEM-ARCHITECTURE MODEL. ╔TS OPERATION
PARALLELS THE IMPLICIT OPERATIONS THAT TAKE PLACE WHEN YOU CALL A LOCAL
PROCEDURE (A SUBROUTINE).
╘HE ╥╨├ MESSAGE-PASSING PARADIGM IS ALSO COUPLED WITH A SHARED-MEMORY PARADIGM
TO OFFER GREATER PERFORMANCE FOR PASSING AROUND MASSIVE AMOUNTS OF DATA. ┴LL
PROCESSES IN THE SYSTEM (AND IN THE ENTIRE DISTRIBUTED SYSTEM WHEN THIS ╧╙ IS
EXTENDED) HAVE GLOBAL ACCESS TO ALL OF THE MEMORY IN THE SYSTEM. ╘HE COUPLING
OF THE TWO PARADIGMS IS SUCH THAT YOU GET THE BEST OF BOTH WORLDS: THE
CONVENENCE AND NATURAL INTERPROCESS *COORDINATION* (SYNCHRONIZATION) SEMANTICS
OF ╥╨├ AND THE CONVENIENCE AND RAW PERFORMANCE OF SHARED STORAGE.
4.1. ═┼╙╙┴╟┼-╨┴╙╙╔╬╟ ├┴╠╠╙
╘HE KERNEL PROVIDES THREE PRIMITIVES FOR MESSAGE PASSING:
├┴╠╠ ╬┴═┼ ╔╬╨╒╘ ┴╥╟╒═┼╬╘╙ ╥┼╘╒╥╬ ╓┴╠╒┼╙
----------- ------------------------------ -------------------------------
╙END ( .┴┘=MSG╚EAD ) .├╙=ERR(.┴=ERRCODE)
╥ECEIVE ( .┴┘=MSG╚EAD ) .┴┘=SENDER╨ID
╥EPLY ( .┴┘=MSG╚EAD[MSG╥ET,MSG─ATA] ) .├╙=ERR(.┴=ERRCODE)
╘HESE CALLS WILL SEND A MESSAGE FROM ONE PROCESS (THE CLIENT) TO ANOTHER
PROCESS (THE SERVER) AND WAIT FOR A REPLY, RECEIVE A MESSAGE FROM ANOTHER
PROCESS (A CLIENT), AND REPLY TO A MESSAGE SENT FROM ANOTHER PROCESS (A
CLIENT) THAT HAS BEEN RECEIVED, RESPECTIVELY.
4.1.1. ═┼╙╙┴╟┼-╚┼┴─┼╥ ─┴╘┴ ╙╘╥╒├╘╒╥┼
┼ACH OF THE MESSAGE-PASSING PRIMITIVES REQUIRES A POINTER TO A MESSAGE-HEADER
DATA STRUCTURE THAT IS STORED IN THE USER PROGRAM'S DATA SPACE. ╘HE MESSAGE
HEADER MUST BE INITIALIZED WITH APPROPRIATE VALUES BEFORE A MESSAGE CAN BE
SENT. ╬OTE THAT THIS SCHEME OF PASSING A POINTER TO A MESSAGE HEADER ALLOWS
YOU TO HAVE MULTIPLE MESSAGE HEADERS LYING AROUND, INITIALIZED AND READY FOR
ACTION, AND YOU CAN EASILY PICK BETWEEN THEM. ╚ERE IS WHAT A MESSAGE HEADER
LOOKS LIKE:
╧╞╞ ╙╔┌ ├╠┴╙╙ ╠┴┬┼╠
--- --- ----- ------
0 2 PID MSG╘O
2 2 PID MSG╞ROM
4 4 BUF MSG┬UF
8 2 BUF MSG╠EN
10 4 BUF MSG╥EP┬UF
14 2 BUF MSG╥EP╠EN
16 1 DATA MSG╧P
17 1 DATA MSG╥ET
18 2 DATA MSG╧BJ
20 4 DATA MSG─ATA
24 - - ╙╔┌┼
┘OU SHOULD NOT PUT TOO MUCH FAITH IN THE OFFSETS OF THE FIELDS IN THE DATA
STRUCTURE REMAINING STATIC; YOU SHOULD ALWAYS USE THE LABEL TO ACCESS THE
FIELDS OF THE STRUCTURE, AS IN:
STA MY═ESSAGE╚EADER+MSG╘O+0
STY MY═ESSAGE╚EADER+MSG╘O+1
4.1.1.1. ╨╔─-├╠┴╙╙ ╞╔┼╠─╙
╘HE FIRST TWO FIELDS, OF CLASS "PID", ARE USED TO IDENTIFY THE PROCESSES
INVOLVED IN AN ╥╨├ INTERACTION. ╘HE "MSG╘O" FIELD IS THE PID OF THE PROCESS
THAT A MESSAGE IS TO BE/HAS BEEN SENT TO, AND THE "PCB╞ROM" FIELD IS THE ID OF
THE PROCESS WHICH A MESSAGE HAS BEEN RECEIVED FROM. ╞OR SECURITY REASONS, THE
SENDER DOES NOT FILL IN THE "PCB╞ROM" FIELD; THE KERNEL DOES AFTER THE MESSAGE
HAS BEEN SENT AND THE SENDER IS BLOCKED. (╧R ELSE THE SENDER COULD FAKE BEING
SOMEONE ELSE). ╘HE "PCB╘O" FIELD IS USED AS THE DESTINATION FOR WHEN A
MESSAGE IS BEING SENT AND MUST BE FILLED IN WITH A LEGITIMATE VALUE ON A SEND
OPERATION, AND THE "PCB╞ROM" FIELD IS USED AS THE DESTINATION WHEN A MESSAGE
IS BEING REPLIED TO, AND MUST BE FILLED IN WITH A LEGITIMATE VALUE ON A REPLY
OPERATION. ╘HE "PCB╘O" FIELD IS THE ONLY FIELD OF THE MESSAGE HEADER THAT
ACTUALLY NEEDS TO HAVE A LEGITIMATE VALUE BEFORE A MESSAGE CAN BE SENT.
4.1.1.1. ┬╒╞-├╠┴╙╙ ╞╔┼╠─╙
╘HE NEXT FOUR FIELDS, OF CLASS "BUF", POINT OUT THE SEND AND REPLY BUFFERS IN
MEMORY AND THE SIZES OF EACH. ╘HE SEND BUFFER ("MSG┬UF"/"MSG╠EN") IS EXPECTED
TO POINT TO A REGION OF NEAR/FAR MEMORY THAT CONTAINS VALID DATA FOR A SEND
OPERATION, AND THE REPLY BUFFER ("MSG╥EP┬UF"/"MSG╥EP╠EN") IS EXPECTED TO POINT
TO A VALID AREA OF MEMORY FOR THE SERVER TO FILL IN WITH ANY BULKY RESULT DATA
FROM AN ╥╨├ REQUEST. ┼ACH OF THE MESSAGE-BUFFER POINTERS IS FOUR BYTES IN
SIZE TO ALLOW FOR FUTURE EXPANSION WHEN THE KERNEL WILL SUPPORT "FAR" MEMORY
THAT WILL BE ACCESSED THROUGH 32-BIT POINTERS. ╒SER PROCESSES ARE EXPECTED TO
ACCESS THESE "FAR" BUFFERS DIRECTLY THEMSELVES, THROUGH THE GLOBAL SHARED
MEMORY. ╘HIS ELIMINATES THE SYSTEM OVERHEAD OF USELESSLY COPYING BULKY DATA
FROM PLACE TO PLACE.
╘HERE ARE TWO SPECIAL NOTES TO MAKE ABOUT THERE "BUF" FIELDS. ╞IRST, THEY
DON'T ACTUALLY HAVE TO BE USED HOW THEY'RE INTENDED TO BE USED. AS LONG AS
BOTH THE CLIENT AND THE SERVER AGREE ON WHAT THE CONTENTS OF THESE FIELDS ARE
SUPPOSED TO MEAN. ╔N THIS RESPECT, THE FIELDS CAN BE USED TO QUICKLY PASS
TWELVE BYTES OF COMPLETELY ARBITRARY INFORMATION. ╘HIS IS USEFUL BECAUSE MANY
╥╨├S ONLY REQUIRE THAT A SMALL AMOUNT OF INFORMATION BE TRANSFERRED FROM ONE
PROCESS TO ANOTHER, OR AT LEAST THAT BULKY DATA BE PASSED IN ONLY ONE
DIRECTION (LIKE READ OR WRITE), SO THAT ONE OF THE BUFFER POINTERS IS FREE TO
BE USED QUICK, TINY DATA.
╙ECOND, ON THE SENDING SIDE, THE "BUFFER" THAT IS POINTED TO DOES NOT HAVE TO
BE A "BUFFER" AT ALL; IT CAN BE AN ARBITRARY DATA STRUCTURE THAT HAS AN
ARBITRARY NUMBER OF PIECES, SCATTERED THROUGHOUT THE GLOBAL MEMORY OF THE
SYSTEM. ╘HE ONLY RESPONSIBILITY OF THE SENDER IS TO INSURE THAT NO ONE ELSE
WILL BE ATTEMPTING TO MODIFY THE SHARED DATA SIMULTANEOUSLY WHILE THE SERVER
IS ACCESSING IT. ╘HIS SCHEME IS QUITE INGENIOUS, ╔ THINK (THANK YOU, THANK
YOU). (╘HE SCHEME MAY APPEAR TO HAVE A SECURITY LEAK IN THE DESIGN, BUT OUR
SYSTEM HAS NO REAL HARDWARE SECURITY ANYWAY).
╘HE EXPECTED USAGE OF BUFFERS WILL BE FOR THE SENDER TO USE NEAR MEMORY FOR
THE REQUEST AND REPLY BUFFERS AND ACCESS THEM AS REGULAR NEAR MEMORY TO
CONSTRUCT AND INTERPRET REQUEST AND REPLY MESSAGES. ╘HE RECEIVER WILL (IN THE
FUTURE) ACCESS THE BUFFERS AS FAR MEMORY (WHICH THEY MAY VERY WELL BE SINCE
PROCESSES WILL BE ALLOWED TO EXECUTE ON DIFFERENT BANKS OF INTERNAL MEMORY AND
EVEN ON DIFFERENT MACHINES), AND MAY WISH TO FETCH PARTS OF MESSAGES INTO NEAR
MEMORY FOR PROCESSING. ╘HE USE OF FAR POINTERS MAKES IT SO THAT DATA IS
COPIED ONLY WHEN NECESSARY, AND COPIED ONLY ONCE.
4.1.1.3. ─┴╘┴-├╠┴╙╙ ╞╔┼╠─╙
╘HE FINAL FOUR FIELDS, OF CLASS "DATA", ARE INTENDED TO BE USED TO
CONVENIENTLY PASS SMALL AMOUNTS OF ARBITRARY DATA. ╘HIS DATA CAN BE
ARBITRARY, BUT THE FIELDS DO HAVE A CONVENTION THAT SHOULD USUALLY BE
FOLLOWED, UNLESS BOTH PARTIES AGREE TO AN ALTERNATIVE USAGE.
╘HE "MSG╧P" FIELD IS INTENDED TO BE THE "OPERATION CODE" THAT A CLIENT PROCESS
WISHES A SERVER TO EXECUTE. ╘HE "MSG╥ET" FIELD IS INTENDED TO BE THE RETURN/
ERROR CODE THAT IS RETURNED FROM THE SERVER TO THE CLIENT UPON COMPLETION OF
AN OPERATION. ╘HE "MSG╧BJ" FIELD IS INTENDED TO BE USED BY THE CLIENT TO
INDICATE WHICH OF THE SERVER'S "OBJECTS" THE CLIENT WISHES TO PERFORM THE
OPERATION ON. ┴ND THE "MSG─ATA" FIELD IS INTENDED TO CONTAIN FOUR BYTES OF
ARBITRARY USER DATA THAT IS PASSED IN WITH AN OPERATION AND IS PASSED BACK
FROM THE SERVER TO GIVE RETURN VALUES. ╔N THE SPIRIT OF THESE SEMANTICS, THE
DATA IN ALL OF THE FIELDS IS SEND WITH A REQUEST, BUT ONLY THE DATA IN THE
"MSG╥ET" AND "MSG─ATA" FIELDS IS PASSED BACK IN A REPLY OPERATION. ╬ONE OF
THE OTHER FIELDS ARE PASSED BACK IN A REPLY OPERATION (THE FIELD VALUES WILL
REMAIN HOW THEY WERE BEFORE THE SEND, FOR THE SENDER). ╘AKE SPECIAL NOTE THAT
THE "MSG╥EP╠EN" FIELD WILL NOT BE PASSED BACK; IF THERE IS LESS DATA RETURNED
THAN WAS ASKED FOR BY AN OPERATION, YOU WILL HAVE TO ENCODE THE "ACTUAL"
REPLY-BUFFER LENGTH INTO THE "MSG─ATA" FIELD.
4.1.2. ╙┼╬─
╙END() IS USED TO TRANSMIT A MESSAGE TO A REMOTE PROCESS AND GET BACK A REPLY
MESSAGE. ╘HE .┴┘ REGISTER CONTAINS THE NEAR-MEMORY ADDRESS OF THE MESSAGE
HEADER, WHICH MUST HAVE ITS "MSG╘O" FIELD FILLED IN TO BE THE PID OF THE
PROCESS THAT THE MESSAGE IS BEING SENT TO. ╘HE SENDING PROCESS WILL SUSPEND
ITS EXECUTION WHILE IT IS WAITING FOR REMOTE PROCESS TO PROCESS ITS REQUEST.
╔F THERE IS TO BE BULKY REPLY DATA FOR THE REQUEST (SUCH AS THERE WOULD BE FOR
A "READ" REQUEST TO A FILE SERVER), THEN SPACE FOR THE REPLY BUFFER MUST BE
ALLOCATED AND INDICATED IN THE MESSAGE HEADER. ╘HE REPLY-BUFFER SPACE SHOULD
NORMALLY BE OWNED BY THE SENDER.
╔F THERE IS AN ERROR IN PASSING THE MESSAGE, THE THE ERROR RETURN WILL BE
INDICATED BY THE CARRY FLAG BEING SET AND THE ERROR CODE WILL BE RETURNED IN
THE .┴ REGISTER. ╙OME POSSIBLE ERRORS WILL BE, IN THE FUTURE: DESTINATION
PROCESS IS NOT VALID, AND THAT DESTINATION PROCESS DIED BEFORE RECEIVING/
REPLYING TO YOUR MESSAGE. (╘HESE CONDITIONS ARE NOT CURRENTLY CHECKED). ┴LSO
IN THE FUTURE, THIS CALL WILL WORK COMPLETELY TRANSPARENTLY FOR PASSING
MESSAGES BETWEEN MACHINES IN A NETWORK.
4.1.3. ╥┼├┼╔╓┼
╥ECEIVE() IS USED TO RECEIVE A MESSAGE TRANSMITTED BY A REMOTE PROCESS TO THE
CURRENT PROCESS. ╘HE RECEIVER WILL BLOCK UNTIL ANOTHER PROCESS DOES A
CORRESPONDING ╙END() OPERATION, AND THEN THE MESSAGE HEADER SENT BY THE SENDER
WILL BE RETRIEVED INTO THE MESSAGE-HEADER BUFFER POINTED TO BY THE .┴┘
REGISTER, FOR THIS CALL. ╬O ERROR RETURNS ARE POSSIBLE. ╘HE PID OF THE
SENDING PROCESS WILL BE RETURNED IN THE .┴┘ REGISTER AS WELL AS IN THE
"MSG╞ROM" FIELD OF THE RECEIVE-MESSAGE-HEADER BUFFER. ╘HE RECEIVER IS THEN
EXPECTED TO EVENTUALLY CALL THE ╥EPLY() PRIMITIVE TO RE-AWAKEN THE SENDER.
╘HE RECEIVER IS FREE TO DO ANYTHING IT WANTS TO AFTER RECEIVING A MESSAGE FROM
A PROCESS, INCLUDING RECEIVING MESSAGES FROM OTHER PROCESSES. ═ESSAGES ARE
RECEIVED FROM OTHER PROCESSES IN ╞╔╞╧ ORDER.
┴ SIMILAR ╥ECEIVE╙PECIFIC() PRIMITIVE MAY BE PROVIDED IN THE FUTURE. ╔T WOULD
ONLY ACCEPT A MESSAGE FROM A SPECIFICALLY NAMED PROCESS AND WOULD ENQUEUE ALL
OTHER MESSAGES THAT ARE RECEIVED BEFORE THE SPECIFIC MESSAGE, TO BE RECEIVED
LATER.
4.1.4. ╥┼╨╠┘
╥EPLY() IS USED TO RE-AWAKEN A PROCESS THAT SENT A MESSAGE THAT WAS ╥ECEIVE()D
BY THE CURRENT PROCESS. ╘HE CURRENT PROCESS IS EXPECTED TO HAVE SET UP THE
RETURN INFORMATION IN THE REPLY-MESSAGE-HEADER BUFFER AND THE REPLY BUFFER
AREA ACCORDING TO THE CLIENT'S WISHES BEFORE CALLING THE ╥EPLY() PRIMITIVE.
╘HE NEAR ADDRESS OF THE REPLY-MESSAGE-HEADER BUFFER IS LOADED INTO THE .┴┘
REGISTER AS AN ARGUMENT TO THE CALL. ╧NLY THE "MSG╞ROM", "MSG╥ET", AND
"MSG─ATA" FIELDS NEED TO HAVE VALUES. ╘HE "MSG╞ROM" FIELD IDENTIFIES THE
PROCESS TO SEND THE REPLY MESSAGE TO, AND THAT PROCESS MUST BE IN THE STATE OF
WAITING FOR A REPLY FROM THE ╥EPLY()ING PROCESS, OR AN ERROR WILL BE
RETURNED. ┴N ERROR IS INDICATED BY THE CARRY FLAG BEING SET ON RETURN AND THE
ERROR CODE IS LOADED IN THE .┴ REGISTER. ╔N THE CASE OF AN ERROR, NO ACTION
WILL HAVE BEEN PERFORMED BY THE SYSTEM.
4.2. ╔═╨╠┼═┼╬╘┴╘╔╧╬
╘HE FIELDS OF THE PROCESS CONTROL BLOCK THAT ARE USED FOR MESSAGE PASSING
ARE RESTATED HERE:
╧╞╞ ╙╔┌ ├╠┴╙╙ ╠┴┬┼╠
--- --- ----- ------
12 2 IPC PCB╙END═SG╨TR (OVERLAP)
12 2 IPC PCB╥ECV═SG╨TR (OVERLAP)
14 2 IPC/Q PCB╙END╤╚EAD
16 2 IPC/Q PCB╙END╤╘AIL
18 1 IPC/Q PCB╙END╤╞LAG
19 1 IPC/Q PCB╙END╤├OUNT
20 2 IPC PCB┬LOCKED╧N
22 2 IPC PCB╥ECEIVE╞ROM
╘HE "PCB┬LOCKED╧N" FIELD IS USED TO ALLOW ╥EPLY() TO VERIFY THAT THE PID IT IS
INSTRUCTED TO SEND A REPLY MESSAGE TO IS INDEED WAITING FOR A REPLY FROM THE
TASK CALLING ╥EPLY(). ╘HE "PCB╙END╤*" FIELDS CONSTITUTE A QUEUE HEAD FOR A
LIST OF PROCESS CONTROL BLOCKS THAT ARE WAITING TO SEND A MESSAGE TO THE
CURRENT PROCESS. ╘HE "PCB╙END═SG╨TR" AND "PCB╥ECV═SG╨TR" FIELDS ARE USED TO
SAVE THE MESSAGE DATA PARAMETERS OF A ╙END() OR ╥ECEIVE() CALL, RESPECTIVELY,
WHEN IT HAS TO BE SUSPENDED WITHOUT A TRANSFER OF THE MESSAGE HEADER. ╫HEN
THE OTHER PROCESS INVOLVED PERFORMS THE CORRESPONDING OPERATION, THE FIRST
PROCESS' HEADER BUFFER POINTER IS RECOVERED FROM ITS PROCESS CONTROL BLOCK.
╘HE "PCB╥ECEIVE╞ROM" FIELD IS UNUSED AT THIS TIME.
╘HE PROCESS STATES OF ╙╘┴╘┼_╙┼╬─, ╙╘┴╘┼_╥┼╨╠┘, AND ╙╘┴╘┼_╥┼├┼╔╓┼ ARE USED WITH
MESSAGE PASSING. ╘HE ╙╘┴╘┼_╙┼╬─ STATE MEANS THAT THE CURRENT PROCESS HAS SENT
A MESSAGE TO A SERVER PROCESS AND IS WAITING FOR IT TO DO A ╥ECEIVE(). ╘HE
╙╘┴╘┼_╥┼╨╠┘ STATE MEANS THAT THE CURRENT PROCESS HAS SENT A MESSAGE TO A
SERVER PROCESS, THE MESSAGE HAS BEEN ╥ECEIVE()D, AND THAT THE CURRENT PROCESS
IS WAITING FOR THE SERVER PROCESS TO PERFORM A ╥EPLY(). ╘HE ╙╘┴╘┼_╥┼├┼╔╓┼
STATE MEANS THAT THE CURRENT PROCESS HAS PERFORMED A ╥ECEIVE() AND IS WAITING
FOR SOME OTHER PROCESS TO PERFORM A CORRESPONDING ╙END(). ╘HESE STATE
NAMES/MEANINGS MAY BE A BIT INCONSISTENT; DEAL WITH IT.
╘HE IMPLEMENTATION OF THE ACTUAL ╙END(), ╥ECEIVE(), AND ╥EPLY() OPERATIONS IS
ACTUALLY QUITE STRAIGHT-FORWARD. ┬OTH ╙END() AND ╥ECEIVE() HAVE TO HANDLE TWO
POSSIBILE SITUATIONS: EITHER THE OTHER PROCESS INVOLVED HAS ALREADY PERFORMED
ITS CORRESPONDING OPERATION AND IS WAITING, OR IT HAS NOT. ╥EPLY() IS
SIMPLIFIED IN THAT IT KNOWS THAT THE SENDER IS ALREADY WAITING FOR ITS REPLY
SO IT CAN PROCEED TO COPY THE REPLY-MESSAGE-HEADER CONTENTS DIRECTLY.
╘HE ╙END() PRIMITIVE (WILL) CHECKS THE GIVEN DESTINATION PID FOR VALIDITY AND
THEN CHECKS THE STATE OF THE RECIPIENT PROCESS. ╔F THE RECIPIENT PROCESS IS
IN ╙╘┴╘┼_╥┼├┼╔╓┼, THE ╙END() FUNCTION COPIES THE MESSAGE-HEADER CONTENTS
DIRECTLY TO THE RECEIVE-HEADER BUFFER OF THE RECIPIENT. ╘HE ADDRESS OF THE
RECEIVE-HEADER BUFFER IS TAKEN FROM THE "PCB╥ECV═SG╨TR" FIELD OF THE
RECEIVER'S PROCESS CONTROL BLOCK IN THIS CASE. ╘HE RECEIVER'S RETURN VALUE
(THE SENDING PROCESS' PID) IS SET UP (ON THE RECEIVING PROCESS' STACK) AND THE
RECEIVER IS AWAKENED WHILE THE SENDER IS PUT TO SLEEP, IN ╙╘┴╘┼_╥┼╨╠┘ STATE
(SINCE THE RECEIVE HAS ALREADY HAPPENED, IT IS WAITING FOR THE CORRESPONDING
╥EPLY()).
╔F THE RECIPIENT PROCESS IS NOT IN THE ╙╘┴╘┼_╥┼├┼╔╓┼ STATE, THEN THE SENDING
PROCESS WILL HAVE TO WAIT FOR THE RECIPIENT TO PERFORM A ╥ECEIVE(). ╘HE
SENDER'S MESSAGE-HEADER BUFFER ADDRESS IS STORED INTO ITS PROCESS CONTROL
BLOCK, THE SENDER'S PROCESS CONTROL BLOCK IS LINKED INTO THE RECIPIENT
PROCESS' "PCB╙END╤*", AND THE SENDER IS PUT TO SLEEP, IN THE ╙╘┴╘┼_╙┼╬─
STATE.
╘HE ╙END() FUNCTION DOES NOT SET UP THE RETURN VALUE FOR THE USER'S
SYSTEM CALL SINCE THAT WILL NOT BE KNOWN UNTIL ANOTHER PROCESS PERFORMS THE
CORRESPONDING ╥EPLY(). ┴ RETURN VALUE IS SET UP IMMEDIATELY ONLY IN THE CASE
OF AN ERROR. ╘HE POSSIBLE ERROR RETURNS FROM ╙END() ARE: INVALID PID AND
REPLY TOO LONG (IN WHICH CASE THE REPLY IS TRUNCATED).
╘HE ╥ECEIVE() PRIMITIVE FIRST CHECKS ITS "PCB╙END╤*" TO SEE IF ANY PROCESSES
HAVE ALREADY TRIED TO SEND A MESSAGE TO THE RECEIVER. ╔F THERE IS A PROCESS
THERE, THE SENDER'S PROCESS CONTROL BLOCK IS REMOVED FROM THE HEAD OF THE SEND
QUEUE THEN THE SENDER PROCESS' STATE IS CHANGED TO ╙╘┴╘┼_╥┼╨╠┘ AND THE SENT
MESSAGE-HEADER CONTENTS (DEREFERENCED BY THE SENDER'S "PCB╙END═SG╨TR" POINTER)
ARE COPIED INTO THE RECEIVER'S MESSAGE-HEADER BUFFER. ╘HE ╥ECEIVE() PRIMITIVE
THEN EXITS RETURNING THE PID OF THE SENDER. ╬O ERROR RETURNS ARE POSSIBLE.
╔F THERE IS NO PROCESS ENQUEUED IN THE RECIPIENT PROCESS' "PCB╙END╤*", THEN
THE RECEIVING PROCESS IS PUT TO SLEEP IN THE ╙╘┴╘┼_╥┼├┼╔╓┼ STATE AND ITS
MESSAGE-HEADER BUFFER POINTER IS COPIED INTO ITS PROCESS CONTROL BLOCK.
╘HE ╥EPLY() PRIMITIVE VERIFIES THAT THE DESTINATION PROCESS IS VALID (BUT NOT
IN THE CURRENT IMPLEMENTATION) AND IS ACTUALLY AWAITING A REPLY FROM THE
REPLYING PROCESS. ╔F NOT, IT CRAPS OUT. ╧THERWISE, IT COPIES THE TWO
MESSAGE-HEADER FIELDS AND AWAKENS THE SENDER. ╘HE RETURN VALUE OF THE SENDER
IS (ALREADY) SET UP TO BE CARRY-CLEAR (NO ERROR) AND THE ╥EPLY() PRIMITIVE
RETURNS ERROR-FREE TOO.
╘HE ┼XIT() KERNEL CALL DOES NOT CURRENTLY RECOVER FROM A PROCESS PERFORMING A
╥ECEIVE() AND THEN ┼XIT()ING BEFORE PERFORMING THE CORRESPONDING ╥EPLY().
╙OME CARE WILL HAVE TO BE TAKEN TO INSURE THAT ALL PROCESS INVOLVED IN ╔╨├ CAN
CONSISTENTLY RECOVER IF ONE OF THE PROCESSES GETS BLOWN AWAY, FOR WHATEVER
REASON (INCLUDING ┼XIT()). ╙UCH CONSISTENT RECOVERY HAS TO BE CAREFULLY
THOUGHT OUT FOR ANY KIND OF OPERATING SYSTEM; HOWEVER, SINCE THERE ARE ONLY A
SMALL NUMBER OF KERNEL CONCEPTS IN THIS ONE, CONSISTENT RECOVERY IS THAT MUCH
EASIER TO INSURE.
5. ├╧╬├╠╒╙╔╧╬
╙O THERE YA HAVE IT; THE START OF A REAL OPERATING SYSTEM FOR THE ├OMMODORE
128. ╫HAT THE OPERATING SYSTEM NEEDS IN TERMS OF FEATURES IS TO BE EXTENDED
TO EXECUTE PROCESSES ON ANY BANK OF INTERNAL MEMORY, TO ACCESS FAR MEMORY, AND
TO BE DISTRIBUTED SO THAT IT WILL WORK ACROSS MULTIPLE HOSTS. ╫HAT IT NEEDS
IN TERMS OF SOFTWARE IS: DEVICE DRIVERS, A COMMAND SHELL, UTILITY PROGRAMS,
AND AN ASSEMBLER THAT CAN PRODUCE RELOCATABLE CODE. ╧H WHERE, OH WHERE SHALL
╔ EVER FIND SUCH SOFTWARE??? ;-)
------------------------------------------------------------------------------
┴╨╨┼╬─╔╪ ┴. ╙╧╒╥├┼-├╧─┼ ╠╔╙╘╔╬╟
╘HE SOURCE CODE FOLLOWS. ┼XTRACT EVERYTHING BETWEEN THE "-----=-----" LINES
AND SAVE INTO A FILE NAMED "BOS.S" (OR WHATEVER) AND THEN RUN IT THROUGH THE
┴├┼ ASSEMBLER TO GENERATE THE EXECUTABLE PROGRAM (WHICH IS ALSO INCLUDED BELOW
FOR YOUR CONVENIENCE). ╘HE ┴├┼ ASSEMBLER IS AVAILABLE FOR FREE WITH THE
┴├┼-128/64 SYSTEM.
╔ HAVE NOT GONE THROUGH AND FULLY DOCUMENTED THE SOURCE CODE, SINCE ╔ HAVE
BEEN SITTING ON THIS PROGRAM FOR QUITE A WHILE AND AM IN A RUSH TO GET IT OUT
THE DOOR. ┬ESIDES, THE FUNCTIONALITY OF EACH IMPORTANT COMPONENT HAS ALREADY
BEEN DISCUSSED.
-----=-----
;SIMPLE MULTITASKING KERNEL BY ├RAIG ┬RUCE, STARTED 25-╧CT-1994.
;╘HIS PROGRAM IS WRITTEN IN THE ┴├┼-┴SSEMBLER FORMAT.
ORG $1300
JMP MAIN
;======== DECLARATIONS ========
PCB╬EXT = 00 ;(2) MGMT
PCB╨REV = 02 ;(2) MGMT
PCB╔S╚EAD = 04 ;(1) MGMT
PCB╤├OUNT = 05 ;(1) MGMT
PCB╙╨ = 06 ;(1) CTXT
PCB╙TACK╨AGE = 07 ;(1) CTXT
PCB┌ERO╨AGE = 08 ;(1) CTXT
PCB─506 = 09 ;(1) CTXT
PCB╨RIORITY = 10 ;(1) SCHE
PCB├OUNTDOWN = 11 ;(1) SCHE
PCB╫AKEUP╘IME = 12 ;(2) SCHE (OVERLAP)
PCB╫AIT┼VENT = 12 ;(1) SCHE (OVERLAP)
PCB╙END═SG╨TR = 12 ;(2) SCHE (OVERLAP)
PCB╥ECV═SG╨TR = 12 ;(2) SCHE (OVERLAP)
PCB╙END╤╚EAD = 14 ;(2) IPC
PCB╙END╤╘AIL = 16 ;(2) IPC
PCB╙END╤╞LAG = 18 ;(1) IPC
PCB╙END╤├OUNT = 19 ;(1) IPC
PCB┬LOCKED╧N = 20 ;(2) IPC
PCB╥ECEIVE╞ROM = 22 ;(2) IPC
PCB╨ARENT = 24 ;(2) PROC
PCB╙TATE = 26 ;(1) PROC
PCB╙IZE = 27
╙╘┴╘┼_╥┼┴─┘ = $C0
╙╘┴╘┼_╙┼╬─ = $C1
╙╘┴╘┼_╥┼├┼╔╓┼ = $C2
╙╘┴╘┼_╥┼╨╠┘ = $C3
╙╘┴╘┼_─┼╠┴┘ = $C4
╙╘┴╘┼_╙╒╙╨┼╬─┼─ = $C5
╙╘┴╘┼_┼╓┼╬╘ = $C6
╦┼╥╬_┼╥╥_╧╦ = $E0
╦┼╥╬_┼╥╥_╨╔─_╬╧╘_╥┼╨╠┘ = $E1
MSG╘O = 0 ;(2)
MSG╞ROM = 2 ;(2)
MSG┬UF = 4 ;(4)
MSG╠EN = 8 ;(2)
MSG╥EP┬UF = 10 ;(4)
MSG╥EP╠EN = 14 ;(2)
MSG╧P = 16 ;(1)
MSG╥ET = 17 ;(1)
MSG╧BJ = 18 ;(2)
MSG─ATA = 20 ;(4)
MSG╙IZE = 24
QUEUE╚EAD╙IZE = 6
NULL╨CB : BUF PCB╙IZE
DELAY╤UEUE : BUF QUEUE╚EAD╙IZE
JIFFY╘IME : BUF 2
ACTIVE╨ID = 02 ;(2)
P = 04 ;(2)
Q = 06 ;(2)
PCB╨TR = 08 ;(2)
MSG╨TR = 10 ;(2)
PAGE┴LLOC = 12 ;(1)
;╙TACK: ($FF) : EXITADDR-1.H
; ($FE) : EXITADDR-1.L
; ($FD) SP+07: PC.H
; ($FC) SP+06: PC.L
; ($FB) SP+05: STATUS REGISTER
; ($FA) SP+04: .┴
; ($F9) SP+03: .╪
; ($F8) SP+02: .┘
; ($F7) SP+01: $FF00 SAVE
; ($F6) SP+00: -EMPTY-
BK┬╧╙ = $0E
BK╒SER = $0E
BK╙ELECT = $FF00
VIC = $D000
SID = $D400
MMU┌ERO╨AGE = $D507
MMU╙TACK╨AGE = $D509
╔RQ┼XIT = $FF33
; ├REATE ( .┴┘=ADDRESS, .╪=PRIORITY ) : .┴┘=PID
; ┼XIT ( .┴=CODE, .╪=$00 )
; ═Y╨ID ( ) : .┴┘=PID
; ═Y╨ARENT╨ID ( ) : .┴┘=PARENT╨ID
; ╙USPEND ( )
; ─ELAY ( .┴┘=JIFFIES ) : .├╙=ERR
; ╙END ( .┴┘=MSG┬UF ) : .├╙:.┴=ERR
; ╥ECEIVE ( .┴┘=MSG┬UF ) : .┴┘=SENDER╨ID
; ╥EPLY ( .┴┘=MSG┬UF[MSG╥ET,MSG─ATA] ) : .├╙:.┴=ERR
;======== KERNEL CODE ========
MAIN = *
SEI
;** ENTRY
LDA #BK┬╧╙
STA BK╙ELECT
;** SET INTERRUPT VECTORS
LDA #<╔RQ╚ANDLER
LDY #>╔RQ╚ANDLER
STA $0314
STY $0315
LDA #<┬RK╚ANDLER
LDY #>┬RK╚ANDLER
STA $0316
STY $0317
LDA #<╬MI╚ANDLER
LDY #>╬MI╚ANDLER
STA $0318
STY $0319
;** INITIALIZE DELAY QUEUE
LDA #0
STA JIFFY╘IME+0
STA JIFFY╘IME+1
LDA #<DELAY╤UEUE
LDY #>DELAY╤UEUE
STA Q+0
STY Q+1
JSR ╤UEUE╔NIT
;** INITIALIZE NULL/BOOT PROCESS
LDA #<NULL╨CB
LDY #>NULL╨CB
STA NULL╨CB+PCB╬EXT+0
STY NULL╨CB+PCB╬EXT+1
STA NULL╨CB+PCB╨REV+0
STY NULL╨CB+PCB╨REV+1
STA ACTIVE╨ID+0
STY ACTIVE╨ID+1
LDA #$FF
STA NULL╨CB+PCB╔S╚EAD
LDA #0
STA NULL╨CB+PCB╤├OUNT
LDA #>$2000
STA PAGE┴LLOC
LDA #╙╘┴╘┼_╥┼┴─┘
STA NULL╨CB+PCB╙TATE
LDA #2
STA NULL╨CB+PCB╨RIORITY
CLI
JMP ╬ULL
╬ULL = *
;** CREATE INIT PROCESS
LDA #<╔NIT
LDY #>╔NIT
LDX #1
JSR ├REATE
;** GO INTO ENDLESS LOOP
- INC $0400
BNE +
INC $0401
BNE +
INC $0402
BNE +
INC $0403
+ JMP -
╬MI╚ANDLER = *
┬RK╚ANDLER = *
╙HUTDOWN = *
;** RESTORE INTERRUPT VECTORS
SEI
LDA #<$FA65
LDY #>$FA65
STA $0314
STY $0315
LDA #<$FA40
LDY #>$FA40
STA $0318
STY $0319
LDX #250
TXS
LDA #$00
STA MMU┌ERO╨AGE
STA MMU┌ERO╨AGE+1
LDX #$01
STX MMU╙TACK╨AGE
STA MMU╙TACK╨AGE+1
LDA #%00000100
STA $D506
CLI
JMP $4DB7
ZP╙AVE : BUF 1
CREATE┴DDR : BUF 2
CREATE╨RIORITY : BUF 1
CREATE┌EROPAGE : BUF 1
CREATE╙TACK : BUF 1
CREATE╨CB : BUF PCB╙IZE
├REATE = * ;( .┴┘=ADDRESS, .╪=PRIORITY ) : .┴┘=PID
SEI
;** SWITCH IN
STA CREATE┴DDR+0
STY CREATE┴DDR+1
STX CREATE╨RIORITY
LDA MMU┌ERO╨AGE
STA ZP╙AVE
LDA #$00
STA MMU┌ERO╨AGE
;** ALLOCATE RESOURCES
LDA #$00
LDY PAGE┴LLOC
STA PCB╨TR+0
STY PCB╨TR+1
INY
STY CREATE┌EROPAGE
INY
STY CREATE╙TACK
INY
STY PAGE┴LLOC
CPY #>$C000
BCC +
BRK ; RECOVER GRACEFULLY FROM THE CONDITION OF RUNNING OUT OF MEMORY
+
;** INITIALIZE PCB
;** PCB╬EXT ;(2) MGMT := 0
;** PCB╨REV ;(2) MGMT := 0
;** PCB╔S╚EAD ;(1) MGMT := 0
;** PCB╤├OUNT ;(1) MGMT := 0
;** PCB╙╨ ;(1) CTXT := $F6
;** PCB╙TACK╨AGE ;(1) CTXT := NEW
;** PCB┌ERO╨AGE ;(1) CTXT := NEW
;** PCB─506 ;(1) CTXT := $04
;** PCB╨RIORITY ;(1) SCHE := GIVEN
;** PCB├OUNTDOWN ;(1) SCHE := PRIORITY
;** PCB╫AKEUP╘IME ;(2) SCHE := 0
;** PCB╙END╤╚EAD ;(2) IPC := ╤UEUE╔NIT
;** PCB╙END╤╘AIL ;(2) IPC := ╤UEUE╔NIT
;** PCB╙END╤╞LAG ;(1) IPC := ╤UEUE╔NIT
;** PCB╙END╤├OUNT ;(1) IPC := ╤UEUE╔NIT
;** PCB┬LOCKED╧N ;(2) IPC := 0
;** PCB╥ECEIVE╞ROM ;(2) IPC := 0
;** PCB╨ARENT ;(2) PROC := CREATOR
;** PCB╙TATE ;(1) PROC := ╙╘┴╘┼_╙╒╙╨┼╬─┼─
LDX #PCB╙IZE-1
LDA #$00
- STA CREATE╨CB,X
DEX
BPL -
LDA #$F6
STA CREATE╨CB+PCB╙╨
LDA CREATE╙TACK
STA CREATE╨CB+PCB╙TACK╨AGE
LDA CREATE┌EROPAGE
STA CREATE╨CB+PCB┌ERO╨AGE
LDA #$04
STA CREATE╨CB+PCB─506
LDA CREATE╨RIORITY
STA CREATE╨CB+PCB╨RIORITY
STA CREATE╨CB+PCB├OUNTDOWN
LDA ACTIVE╨ID+0
LDY ACTIVE╨ID+1
STA CREATE╨CB+PCB╨ARENT+0
STY CREATE╨CB+PCB╨ARENT+1
LDA #╙╘┴╘┼_╙╒╙╨┼╬─┼─
STA CREATE╨CB+PCB╙TATE
LDY #PCB╙IZE-1
- LDA CREATE╨CB,Y
STA (PCB╨TR),Y
DEY
BPL -
LDA PCB╨TR+0
CLC
ADC #PCB╙END╤╚EAD
STA Q+0
LDA PCB╨TR+1
ADC #0
STA Q+1
JSR ╤UEUE╔NIT
;** INITIALIZE NEW STACK
;** ╙TACK: ($FF) : EXITADDR-1.H := >┼XIT┴DDR
;** ($FE) : EXITADDR-1.L := <┼XIT┴DDR
;** ($FD) SP+07: PC.H := >┴DDR
;** ($FC) SP+06: PC.L := <┴DDR
;** ($FB) SP+05: STATUS REGISTER := $00
;** ($FA) SP+04: .┴ := $00
;** ($F9) SP+03: .╪ := $00
;** ($F8) SP+02: .┘ := $00
;** ($F7) SP+01: $FF00 SAVE := $0E
;** ($F6) SP+00: -EMPTY-
LDA #$00
LDY CREATE╙TACK
STA P+0
STY P+1
LDY #$F6+1
LDA #BK╒SER
STA (P),Y ;$FF00
INY
LDX #4
LDA #$00
- STA (P),Y
INY
DEX
BNE -
LDA CREATE┴DDR+0
STA (P),Y
INY
LDA CREATE┴DDR+1
STA (P),Y
INY
LDA #<─EFAULT┼XIT-1
STA (P),Y
INY
LDA #>─EFAULT┼XIT-1
STA (P),Y
;** MAKE NEW PROCESS READY
JSR ═AKE╥EADY
;** SWITCH OUT
LDA PCB╨TR+0
LDY PCB╨TR+1
LDX ZP╙AVE
STX MMU┌ERO╨AGE
CLC
CLI
RTS
═AKE╥EADY = * ;( (PCB╨TR)=PCB ) ;AFTER ACTIVE╨ID
LDY #PCB╙TATE
LDA #╙╘┴╘┼_╥┼┴─┘
STA (PCB╨TR),Y
LDA #<NULL╨CB
LDY #>NULL╨CB
STA Q+0
STY Q+1
LDA ACTIVE╨ID+0
LDY ACTIVE╨ID+1
STA P+0
STY P+1
JSR ╤UEUE╔NSERT
RTS
╤UEUE╔NIT = * ;( (Q)=QUEUE╚EAD )
LDA Q+0
LDY Q+1
STA QUEUE╔NIT╓ALS+PCB╬EXT+0
STY QUEUE╔NIT╓ALS+PCB╬EXT+1
STA QUEUE╔NIT╓ALS+PCB╨REV+0
STY QUEUE╔NIT╓ALS+PCB╨REV+1
LDA #$FF
STA QUEUE╔NIT╓ALS+PCB╔S╚EAD
LDA #0
STA QUEUE╔NIT╓ALS+PCB╤├OUNT
LDY #QUEUE╚EAD╙IZE-1
- LDA QUEUE╔NIT╓ALS,Y
STA (Q),Y
DEY
BPL -
RTS
QUEUE╔NIT╓ALS : BUF QUEUE╚EAD╙IZE
╤UEUE╔NSERT = * ;( (Q)=QUEUE╚EAD, (P)=NODE╘O╔NSERT┴FTER, (PCB╨TR)=NEW╔TEM )
;** Q->COUNT +:= 1
CLC
LDY #PCB╤├OUNT
LDA (Q),Y
ADC #1
STA (Q),Y
;** PCB╨TR->NEXT := P->NEXT
LDY #PCB╬EXT
LDA (P),Y
STA (PCB╨TR),Y
INY
LDA (P),Y
STA (PCB╨TR),Y
;** PCB╨TR->PREV := P
INY
LDA P+0
STA (PCB╨TR),Y
INY
LDA P+1
STA (PCB╨TR),Y
;** P->NEXT->PREV := PCB╨TR
LDY #PCB╬EXT
LDA (P),Y
STA Q+0
INY
LDA (P),Y
STA Q+1
LDY #PCB╨REV
LDA PCB╨TR+0
STA (Q),Y
INY
LDA PCB╨TR+1
STA (Q),Y
;** P->NEXT := PCB╨TR
LDY #PCB╬EXT
LDA PCB╨TR+0
STA (P),Y
INY
LDA PCB╨TR+1
STA (P),Y
RTS
╤UEUE╒NLINK = * ;( (Q)=QUEUE╚EAD, (PCB╨TR)=NODE ) ;USES P
;** PCB╨TR->NEXT->PREV := PCB╨TR->PREV
LDY #PCB╬EXT
LDA (PCB╨TR),Y
STA P+0
INY
LDA (PCB╨TR),Y
STA P+1
LDY #PCB╨REV
LDA (PCB╨TR),Y
STA (P),Y
INY
LDA (PCB╨TR),Y
STA (P),Y
;** PCB╨TR->PREV->NEXT := PCB╨TR->NEXT
LDY #PCB╨REV
LDA (PCB╨TR),Y
STA P+0
INY
LDA (PCB╨TR),Y
STA P+1
LDY #PCB╬EXT
LDA (PCB╨TR),Y
STA (P),Y
INY
LDA (PCB╨TR),Y
STA (P),Y
;** Q->COUNT -:= 1
LDY #PCB╤├OUNT
LDA (Q),Y
SEC
SBC #1
STA (Q),Y
RTS
╔RQ╚ANDLER = *
CLD
LDA #BK┬╧╙
STA BK╙ELECT
LDA VIC+$19
BPL +
AND #1
BNE ╙IXTY
+ LDA $DC0D
╙IXTY = *
STA VIC+$19
;** SAVE FULL CONTEXT
LDA MMU┌ERO╨AGE
LDX #$00
STX MMU┌ERO╨AGE
LDY #PCB┌ERO╨AGE
STA (ACTIVE╨ID),Y
LDY #PCB╙╨
TSX
TXA
STA (ACTIVE╨ID),Y
LDY #PCB╙TACK╨AGE
LDA MMU╙TACK╨AGE
STA (ACTIVE╨ID),Y
LDY #PCB─506
LDA $D506
STA (ACTIVE╨ID),Y
;** PROCESS INTERRUPT
INC JIFFY╘IME+0
BNE +
INC JIFFY╘IME+1
+ LDA DELAY╤UEUE+PCB╤├OUNT
BEQ +
JSR ─ELAY╔RQ┴WAKE
+ NOP
;** SELECT NEW PROCESS
- LDY #PCB╨RIORITY ;GIVE CUR FULL COUNT
LDA (ACTIVE╨ID),Y
INY
STA (ACTIVE╨ID),Y
BEQ ++
- LDY #PCB╬EXT ;FIND NEXT PROC
LDA (ACTIVE╨ID),Y
TAX
INY
LDA (ACTIVE╨ID),Y
STX ACTIVE╨ID+0
STA ACTIVE╨ID+1
┼XIT╦ERNEL = *
LDY #PCB├OUNTDOWN
LDA (ACTIVE╨ID),Y
BEQ ++
SEC
SBC #1
STA (ACTIVE╨ID),Y
BEQ +
JMP -
+ ;CHECK IF NULL PROCESS
LDY #PCB╔S╚EAD
LDA (ACTIVE╨ID),Y
BPL +
INY
LDA (ACTIVE╨ID),Y ;ONLY RUN NULL IF ONLY PROC
BNE --
+ ;WE'VE GOT A WINNER
;** RESTORE FULL CONTEXT AND EXIT
LDY #PCB─506
LDA (ACTIVE╨ID),Y
STA $D506
LDY #PCB╙TACK╨AGE
LDA (ACTIVE╨ID),Y
STA MMU╙TACK╨AGE
LDY #PCB╙╨
LDA (ACTIVE╨ID),Y
TAX
TXS
LDY #PCB┌ERO╨AGE
LDA (ACTIVE╨ID),Y
STA MMU┌ERO╨AGE
JMP ╔RQ┼XIT
─EFAULT┼XIT = *
LDA #$00
LDX #$00
┼XIT = * ;( .┴=CODE, .╪=$00 )
JMP ╙USPEND
BRK
═Y╨ID = * ;( ) : .┴┘=PID
LDA #$00
LDX MMU┌ERO╨AGE
STA MMU┌ERO╨AGE
LDA ACTIVE╨ID+0
LDY ACTIVE╨ID+1
STX MMU┌ERO╨AGE
CLC
RTS
═Y╨ARENT╨ID = * ;( ) : .┴┘=PARENT╨ID
LDA #$00
LDX MMU┌ERO╨AGE
STA MMU┌ERO╨AGE
LDY #PCB╨ARENT
LDA (ACTIVE╨ID),Y
PHA
INY
LDA (ACTIVE╨ID),Y
TAY
PLA
STX MMU┌ERO╨AGE
CLC
RTS
ENTER╦ERN╙AVE : BUF 4
┼NTER╦ERNEL = *
;** SET UP PROCESS STACK AS IF IT HAD PERFORMED AN INTERRUPT
;** NECESSARY IF PROCESS WILL BLOCK
;** CALLED AS A ONE-LEVEL-DEEP SUBROUTINE OF THE SYSTEM CALL
STA ENTER╦ERN╙AVE+2
;** SAVE SYSTEM-CALL RETURN ADDRESS
PLA
STA ENTER╦ERN╙AVE+0
PLA
STA ENTER╦ERN╙AVE+1
;** INCREMENT USER-PROCESS RETURN ADDRESS (RTS -> RTI)
PLA
CLC
ADC #1
STA ENTER╦ERN╙AVE+3
PLA
ADC #0
PHA
LDA ENTER╦ERN╙AVE+3
PHA
;** SET UP PROCESSOR REGISTERS AS-IS, STATUS $00
LDA #$00
PHA
LDA ENTER╦ERN╙AVE+2
PHA
TXA
PHA
TYA
PHA
LDA $FF00 ;XXX CHANGE FOR MULTI-BANKS
PHA
;** SAVE INFO INTO PCB
LDA MMU┌ERO╨AGE
LDX #$00
STX MMU┌ERO╨AGE
LDY #PCB┌ERO╨AGE
STA (ACTIVE╨ID),Y
DEY
LDA MMU╙TACK╨AGE
STA (ACTIVE╨ID),Y
DEY
TSX
TXA
STA (ACTIVE╨ID),Y
LDY #PCB─506
LDA $D506
STA (ACTIVE╨ID),Y
;** RESTORE SYSTEM-CALL RETURN ADDRESS
;** (CONTINUE TO USE USER-PROCESS STACK)
LDA ENTER╦ERN╙AVE+1
PHA
LDA ENTER╦ERN╙AVE+0
PHA
RTS
╙USPEND = * ;( ) ;SUSPEND SELF
SEI
JSR ┼NTER╦ERNEL
JSR ╙USPEND╙UB
JMP ┼XIT╦ERNEL
╙USPEND╙UB = * ;( ACTIVE╨ID ) : ACTIVE╨ID, PCB╨TR, Q=NULL╨CB
;** ╥EMOVE THE ACTIVE PID FROM THE READY QUEUE AND SET ANOTHER PID TO
;** ACTIVE; SET PCB╨TR TO POINT TO THE SUSPENDED PROCESS; AND SET THE
;** PROCESS STATE TO "SUSPENDED".
LDA ACTIVE╨ID+0
STA PCB╨TR+0
LDA ACTIVE╨ID+1
STA PCB╨TR+1
LDA #<NULL╨CB
LDY #>NULL╨CB
STA Q+0
STY Q+1
JSR ╤UEUE╒NLINK
LDY #PCB╬EXT
LDA (PCB╨TR),Y
STA ACTIVE╨ID+0
INY
LDA (PCB╨TR),Y
STA ACTIVE╨ID+1
LDY #PCB╙TATE
LDA #╙╘┴╘┼_╙╒╙╨┼╬─┼─
STA (PCB╨TR),Y
RTS
─ELAY = * ;( .┴┘=JIFFIES ) : .├╙=ERR
CMP #0
BNE +
CPY #0
BNE +
CLC
RTS
+ SEI
STA DELAY╘IME+0
STY DELAY╘IME+1
JSR ┼NTER╦ERNEL
JSR ╙USPEND╙UB
LDY #PCB╙TATE
LDA #╙╘┴╘┼_─┼╠┴┘
LDY #PCB╫AKEUP╘IME
CLC
LDA DELAY╘IME+0
ADC JIFFY╘IME+0
STA DELAY╘IME+0
STA (PCB╨TR),Y
INY
LDA DELAY╘IME+1
ADC JIFFY╘IME+1
STA DELAY╘IME+1
STA (PCB╨TR),Y
LDA #0
ROL
STA DELAY╘IME+2
LDA #<DELAY╤UEUE
LDY #>DELAY╤UEUE
STA Q+0
STY Q+1
STA P+0
STY P+1
JSR ─ELAY╞IND╙POT
JSR ╤UEUE╔NSERT
JMP ┼XIT╦ERNEL
DELAY╘IME : BUF 3
P╘IME╚I : BUF 1
─ELAY╞IND╙POT = * ;( (Q)=QUEUE, (P)=QUEUE╚EAD, (PCB╨TR) ) : P=PREV╬ODE
JSR ╔NC╨TR╨
LDY #PCB╔S╚EAD
LDA (P),Y
BNE ─ELAY╞IND╙POT┼XIT
LDY #PCB╫AKEUP╘IME
LDA (P),Y
CMP JIFFY╘IME+0
INY
LDA (P),Y
SBC JIFFY╘IME+1
LDX #0
BCS +
INX
+ STX P╘IME╚I
DEY
LDA DELAY╘IME+0
CMP (P),Y
INY
LDA DELAY╘IME+1
SBC (P),Y
LDA DELAY╘IME+2
SBC P╘IME╚I
BCS ─ELAY╞IND╙POT
─ELAY╞IND╙POT┼XIT = *
;XX FALL THROUGH
─EC╨TR╨ = * ;( (P) ) : (P):=(P)->PREV
LDY #PCB╨REV
LDA (P),Y
TAX
INY
LDA (P),Y
STX P+0
STA P+1
RTS
╔NC╨TR╨ = * ;( (P) ) : (P):=(P)->NEXT
LDY #PCB╬EXT
LDA (P),Y
TAX
INY
LDA (P),Y
STX P+0
STA P+1
RTS
─ELAY╔RQ┴WAKE = *
LDA DELAY╤UEUE+PCB╬EXT+0
LDY DELAY╤UEUE+PCB╬EXT+1
STA PCB╨TR+0
STY PCB╨TR+1
LDY #PCB╫AKEUP╘IME
LDA (PCB╨TR),Y
CMP JIFFY╘IME+0
BEQ +
RTS
+ INY
LDA (PCB╨TR),Y
CMP JIFFY╘IME+1
BEQ +
RTS
+ LDA #<DELAY╤UEUE
LDY #>DELAY╤UEUE
STA Q+0
STY Q+1
JSR ╤UEUE╒NLINK
JSR ═AKE╥EADY
JMP ─ELAY╔RQ┴WAKE
MSG╨TR╙AVE : BUF 2
╙END = * ;( .┴┘=MSG┬UF ) : .├╙:.┴=ERR
SEI
STA MSG╨TR╙AVE+0
STY MSG╨TR╙AVE+1
JSR ┼NTER╦ERNEL
JSR ╙USPEND╙UB
LDA MSG╨TR╙AVE+0
LDY MSG╨TR╙AVE+1
STA MSG╨TR+0
STY MSG╨TR+1
LDY #MSG╘O
LDA (MSG╨TR),Y
STA Q+0
INY
LDA (MSG╨TR),Y
STA Q+1
;XX SHOULD VERIFY THAT RECEIVER IS A PROCESS
LDY #PCB╙END═SG╨TR
LDA MSG╨TR+0
STA (PCB╨TR),Y
INY
LDA MSG╨TR+1
STA (PCB╨TR),Y
LDY #PCB┬LOCKED╧N
LDA Q+0
STA (PCB╨TR),Y
INY
LDA Q+1
STA (PCB╨TR),Y
LDY #PCB╙TATE
LDA (Q),Y
CMP #╙╘┴╘┼_╥┼├┼╔╓┼
BEQ ╙END╘O╥ECEIVER┬LOCKED
LDA #╙╘┴╘┼_╙┼╬─
STA (PCB╨TR),Y
CLC
LDA Q+0
ADC #PCB╙END╤╚EAD
STA Q+0
BCC +
INC Q+1
+ LDY #PCB╨REV
LDA (Q),Y
STA P+0
INY
LDA (Q),Y
STA P+1
JSR ╤UEUE╔NSERT
JMP ┼XIT╦ERNEL
╙END╘O╥ECEIVER┬LOCKED = *
LDA #╙╘┴╘┼_╥┼╨╠┘
STA (PCB╨TR),Y
LDY #PCB╥ECV═SG╨TR
LDA (Q),Y
STA P+0
INY
LDA (Q),Y
STA P+1
JSR ├OPY═ESSAGE
LDA PCB╨TR+0
LDY PCB╨TR+1
LDX Q+0
STX PCB╨TR+0
LDX Q+1
STX PCB╨TR+1
LDX #$00
CLC
JSR ╙ET╥ETURN
JSR ═AKE╥EADY
JMP ┼XIT╦ERNEL
SETRET╙AVE : BUF 4
╙ET╥ETURN = * ;( (PCB╨TR)=PROC, .┴╪┘=REGVALS, .├=CVAL ) : (P)=JUNK
STA SETRET╙AVE+2
STX SETRET╙AVE+1
STY SETRET╙AVE+0
PHP
PLA
AND #$01
STA SETRET╙AVE+3
LDY #PCB╙TACK╨AGE
LDA (PCB╨TR),Y
STA P+1
LDY #PCB╙╨
LDA (PCB╨TR),Y
CLC
ADC #2
STA P+0
LDY #3
- LDA SETRET╙AVE,Y
STA (P),Y
DEY
BPL -
RTS
├OPY═ESSAGE = * ;( (PCB╨TR)=SENDER, (MSG╨TR)=SENDMSG, (P)=RECVMSG )
LDY #MSG╞ROM
LDA PCB╨TR+0
STA (MSG╨TR),Y
INY
LDA PCB╨TR+1
STA (MSG╨TR),Y
LDY #MSG╙IZE-1
- LDA (MSG╨TR),Y
STA (P),Y
DEY
BPL -
RTS
╥ECEIVE = * ;( .┴┘=MSG┬UF ) : .┴┘=SENDER╨ID
SEI
STA MSG╨TR╙AVE+0
STY MSG╨TR╙AVE+1
LDA MMU┌ERO╨AGE
PHA
LDA #$00
STA MMU┌ERO╨AGE
LDY #PCB╙END╤├OUNT
LDA (ACTIVE╨ID),Y
BNE ╥ECEIVE╞ROM╙ENDER
PLA
STA MMU┌ERO╨AGE
JSR ┼NTER╦ERNEL
JSR ╙USPEND╙UB
LDA #╙╘┴╘┼_╥┼├┼╔╓┼
STA (PCB╨TR),Y
LDY #PCB╥ECV═SG╨TR
LDA MSG╨TR╙AVE+0
STA (PCB╨TR),Y
INY
LDA MSG╨TR╙AVE+1
STA (PCB╨TR),Y
JMP ┼XIT╦ERNEL
╥ECEIVE╞ROM╙ENDER = * ;( (ACTIVE╨ID), (MSG╨TR╙AVE) )
LDA ACTIVE╨ID+0
LDY ACTIVE╨ID+1
CLC
ADC #PCB╙END╤╚EAD
BCC +
INY
+ STA Q+0
STY Q+1
LDY #PCB╙END╤╚EAD
LDA (ACTIVE╨ID),Y
STA PCB╨TR+0
INY
LDA (ACTIVE╨ID),Y
STA PCB╨TR+1
JSR ╤UEUE╒NLINK ;( (Q)=QUEUE╚EAD, (PCB╨TR)=NODE ) ;USES P
LDY #PCB╙END═SG╨TR
LDA (PCB╨TR),Y
STA MSG╨TR+0
INY
LDA (PCB╨TR),Y
STA MSG╨TR+1
LDA MSG╨TR╙AVE+0
LDY MSG╨TR╙AVE+1
STA P+0
STY P+1
JSR ├OPY═ESSAGE ;( (PCB╨TR)=SENDER, (MSG╨TR)=SENDMSG, (P)=RECVMSG )
LDY #PCB╙TATE
LDA #╙╘┴╘┼_╥┼╨╠┘
STA (PCB╨TR),Y
LDX PCB╨TR+0
LDY PCB╨TR+1
PLA
STA MMU┌ERO╨AGE
TXA
CLI
CLC
RTS
ZP╨TR╙AVE : BUF 1
╥EPLY = * ;( .┴┘=MSG┬UF[MSG╥ET,MSG─ATA] ) : .├╙:.┴=ERR
SEI
;** SWITCH TO KERNEL
LDX MMU┌ERO╨AGE
STX ZP╨TR╙AVE
LDX #$00
STX MMU┌ERO╨AGE
STA MSG╨TR+0
STY MSG╨TR+1
;** FIND AND CHECK THE SENDER
LDY #MSG╞ROM
LDA (MSG╨TR),Y
STA PCB╨TR+0
INY
LDA (MSG╨TR),Y
STA PCB╨TR+1
;XX VERIFY THAT RECEIVER IS A PCB HERE
LDY #PCB╙TATE
LDA (PCB╨TR),Y
CMP #╙╘┴╘┼_╥┼╨╠┘
BEQ +
- LDA #╦┼╥╬_┼╥╥_╨╔─_╬╧╘_╥┼╨╠┘
LDX ZP╨TR╙AVE
STX MMU┌ERO╨AGE
SEC
CLI
RTS
+ LDY #PCB┬LOCKED╧N
LDA (PCB╨TR),Y
CMP ACTIVE╨ID+0
BNE -
INY
LDA (PCB╨TR),Y
CMP ACTIVE╨ID+1
BNE -
;** COPY THE REPLY CONTENTS
LDY #PCB╙END═SG╨TR
LDA (PCB╨TR),Y
STA P+0
INY
LDA (PCB╨TR),Y
STA P+1
LDY #MSG╥ET
LDA (MSG╨TR),Y
STA (P),Y
LDY #MSG─ATA
- LDA (MSG╨TR),Y
STA (P),Y
INY
CPY #MSG─ATA+4
BCC -
;** WAKE UP THE SENDER AND EXIT
JSR ═AKE╥EADY
LDX ZP╨TR╙AVE
STX MMU┌ERO╨AGE
CLC
CLI
RTS
;======== TEST APPLICATION ========
TEST╬UMBER : BUF 1
╔NIT = *
LDA #1
STA TEST╬UMBER
LDA #<╘EST╙ID1
LDY #>╘EST╙ID1
LDX #2
JSR ├REATE
LDA #<╘EST─ELAY1
LDY #>╘EST─ELAY1
LDX #1
JSR ├REATE
LDA #<╘EST─ELAY2
LDY #>╘EST─ELAY2
LDX #1
JSR ├REATE
LDA #<╘EST─ELAY3
LDY #>╘EST─ELAY3
LDX #1
JSR ├REATE
LDA #<╘EST─ELAY4
LDY #>╘EST─ELAY4
LDX #1
JSR ├REATE
LDA #<╘EST─ELAY5
LDY #>╘EST─ELAY5
LDX #1
JSR ├REATE
LDA #<┬LABBER1
LDY #>┬LABBER1
LDX #1
JSR ├REATE
LDA #<╙PINNER1
LDY #>╙PINNER1
LDX #1
JSR ├REATE
JMP ╦ERNEL╙ERVER
╘EST╙ID1 = *
LDX #$1C-1
LDA #$00
- STA $D400,X
DEX
BPL -
LDA #$50
STA 2
STA 3
LDA #$08
STA $D418
LDA #$00
LDY #$08
STA $D402
STY $D403
LDA #$41
STA $D404
LDA #$00
STA $D405
LDA #$F0
STA $D406
- LDA 2
LDY 3
STA $D400
STY $D401
LDA 2
ORA 3
BNE +
LDA #120
LDY #0
JSR ─ELAY
+ INC 2
BNE +
INC 3
+ INC $D020
TSX
JMP -
╘EST─ELAY1 = *
JSR ═Y╨ARENT╨ID
STA TEST─ELAY1═SG+MSG╘O+0
STY TEST─ELAY1═SG+MSG╘O+1
LDA #<TEST─ELAY1╘XT
LDY #>TEST─ELAY1╘XT
STA TEST─ELAY1═SG+MSG┬UF+0
STY TEST─ELAY1═SG+MSG┬UF+1
- LDA #<60
LDY #>60
JSR ─ELAY
INC $581
LDA #<TEST─ELAY1═SG
LDY #>TEST─ELAY1═SG
JSR ╙END
JMP -
TEST─ELAY1╘XT : DB "╚I, THIS IS DELAY PROCESS 1 *\N",0
╘EST─ELAY2 = *
JSR ═Y╨ARENT╨ID
STA TEST─ELAY2═SG+MSG╘O+0
STY TEST─ELAY2═SG+MSG╘O+1
LDA #<TEST─ELAY2╘XT
LDY #>TEST─ELAY2╘XT
STA TEST─ELAY2═SG+MSG┬UF+0
STY TEST─ELAY2═SG+MSG┬UF+1
- LDA #<120
LDY #>120
JSR ─ELAY
INC $582
LDA #<TEST─ELAY2═SG
LDY #>TEST─ELAY2═SG
JSR ╙END
JMP -
TEST─ELAY2╘XT : DB "╚I, THIS IS DELAY PROCESS 2\N",0
╘EST─ELAY3 = *
JSR ═Y╨ARENT╨ID
STA TEST─ELAY3═SG+MSG╘O+0
STY TEST─ELAY3═SG+MSG╘O+1
LDA #<TEST─ELAY3╘XT
LDY #>TEST─ELAY3╘XT
STA TEST─ELAY3═SG+MSG┬UF+0
STY TEST─ELAY3═SG+MSG┬UF+1
- LDA #<180
LDY #>180
JSR ─ELAY
INC $583
LDA #<TEST─ELAY3═SG
LDY #>TEST─ELAY3═SG
JSR ╙END
JMP -
TEST─ELAY3╘XT : DB "╚I, THIS IS DELAY PROCESS 3\N",0
╘EST─ELAY4 = *
JSR ═Y╨ARENT╨ID
STA TEST─ELAY4═SG+MSG╘O+0
STY TEST─ELAY4═SG+MSG╘O+1
LDA #<TEST─ELAY4╘XT
LDY #>TEST─ELAY4╘XT
STA TEST─ELAY4═SG+MSG┬UF+0
STY TEST─ELAY4═SG+MSG┬UF+1
- LDA #<240
LDY #>240
JSR ─ELAY
INC $584
LDA #<TEST─ELAY4═SG
LDY #>TEST─ELAY4═SG
JSR ╙END
JMP -
TEST─ELAY4╘XT : DB "╚I, THIS IS DELAY PROCESS 4\N",0
╘EST─ELAY5 = *
JSR ═Y╨ARENT╨ID
STA TEST─ELAY5═SG+MSG╘O+0
STY TEST─ELAY5═SG+MSG╘O+1
LDA #<TEST─ELAY5╘XT
LDY #>TEST─ELAY5╘XT
STA TEST─ELAY5═SG+MSG┬UF+0
STY TEST─ELAY5═SG+MSG┬UF+1
- LDA #<300
LDY #>300
JSR ─ELAY
INC $585
LDA #<TEST─ELAY5═SG
LDY #>TEST─ELAY5═SG
JSR ╙END
JMP -
TEST─ELAY5╘XT : DB "╚I, THIS IS DELAY PROCESS 5\N",0
┬LABBER1 = *
JSR ═Y╨ARENT╨ID
STA BLABBER1═SG+MSG╘O+0
STY BLABBER1═SG+MSG╘O+1
LDA #<BLABBER1╘XT
LDY #>BLABBER1╘XT
STA BLABBER1═SG+MSG┬UF+0
STY BLABBER1═SG+MSG┬UF+1
- INC $580
LDA #<BLABBER1═SG
LDY #>BLABBER1═SG
JSR ╙END
JMP -
BLABBER1╘XT : DB "╚I, THIS IS BLABBER\N",0
╙PINNER1 = *
JSR ═Y╨ARENT╨ID
STA SPINNER1═SG+MSG╘O+0
STY SPINNER1═SG+MSG╘O+1
LDA #<SPINNER1╘XT
LDY #>SPINNER1╘XT
STA SPINNER1═SG+MSG┬UF+0
STY SPINNER1═SG+MSG┬UF+1
- INC $580
LDA #<SPINNER1═SG
LDY #>SPINNER1═SG
JSR ╙END
JMP -
SPINNER1╘XT : DB "╚I, THIS IS SPINNER +\N",0
╦ERNEL╙ERVER = *
LDA #$00
STA MMU┌ERO╨AGE
LDA #14
JSR $FFD2
- LDA #<KS═SG
LDY #>KS═SG
JSR ╥ECEIVE
LDA KS═SG+MSG┬UF+0
LDY KS═SG+MSG┬UF+1
STA $80
STY $81
LDY #0
- LDA ($80),Y
BEQ +
JSR $FFD2
INY
BNE -
+ LDA #<KS═SG
LDY #>KS═SG
JSR ╥EPLY
JMP --
BSS = *
TEST─ELAY1═SG = $C00 ;** PUT THESE HERE TO SAVE PGM MEMORY
TEST─ELAY2═SG = TEST─ELAY1═SG+MSG╙IZE
TEST─ELAY3═SG = TEST─ELAY2═SG+MSG╙IZE
TEST─ELAY4═SG = TEST─ELAY3═SG+MSG╙IZE
TEST─ELAY5═SG = TEST─ELAY4═SG+MSG╙IZE
BLABBER1═SG = TEST─ELAY5═SG+MSG╙IZE
SPINNER1═SG = BLABBER1═SG+MSG╙IZE
KS═SG = SPINNER1═SG+MSG╙IZE
-----=-----
┴╨╨┼╬─╔╪ ┬. ╒╒┼╬├╧─┼─ ─┼═╧ ╨╥╧╟╥┴═
╘HE UUENCODED DEMO SYSTEM FOLLOWS. ┘OU CAN EXTRACT IT WITH ANY UUDECODER OR
WITH VERSION 2.00 OR HIGHER OF "UNBCODE" (┴├┼ HAS ONLY VERSION 1.00).
-NUCODE-BEGIN 1 BOS
BEGIN 640 BOS
═└!-,)┴,└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└'┬╔#╚╘└
═_┌┼╓╚!6-%└.,%0.╔╩┌└3├18#├!<#╩:╬@$╪╘8└╪╨9└┌─└├203├243╩1┌@$╪4&
═┴└<@╒12╔└┌└3├0,3├└03├043├└83┴0*$└┌╟_├0<3╩0"-"!.╔((4,╩<"-'1.╔
═└╚╘-$╒┴,├1.╔.┌└9╚@$@_1/╬└└30#>╪!!-└([@($╘└/╬└╨1,┼┴-╪╩66@^╚╘4
═└╪╨5└┌┼└╚/╩-&└.,&0.┬^╔╩╔└(╘'╒8╘(╒:(!├@╟5├0╦5╩02-!═583+=-└└└└
═└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└└>(╫=$╪╙>$╪[?$┌╘'╒8╫<
═$┌─└├0?5╩0"─#(4(┴└╟(├.└3╥(╙┴$\┬$#,#└─└$└╚┴╩╔└)╫┬$\╚0^╩╟╓├>@3
═╦>$3├>─3╦>└3├>╚3╩02-┌╤.═╫╤.-[!.-[1.┼└╩0#├?╚3├/╠3╩<6-_!.@&╦╟┬
═$┘$(┬!#╪╔0@8:0┌%!╩4):0"%!╥#5%*─└╦.$3┴02$!:#╫╩0┌1!,┬┬!*─└─03(
═╥═#┌╦=╘3─03(╦=╪3─03(╩0╞1!,┬╔%╔$$(+╠4╔0┬─":[<$╪╪'╒1┴88*└:╩<"1
═"*─#╚!.%!╚0'╔0*─└╪4$┴└4@└!5@╔0:─!╪╫┌%(╙[%(╫\%(╙]%*╟_├?╪4╩0"-
═_╤2@!;╟┌%)$&┬!#╪8└└└└└└└└!┬@!;$&:0&1!╩└└╠021",┬╤!)$(╥*4$─0├(
═╔061"*└└╠02%!╠┬╤!(4'╚└*┼")$&╥*4)─0:@└*4(─03(╔0╞1!&"@└+$(┴03(
═╠0┬%!:└"╠0┬1!,┬╤")$$╚└*╤"(4$╥+$(┴06@└+$(─03(╠0┬1!*└%╠08╪┌0&1
═!╞#8╩0┌-└/^═&=└0!"─!╘└.═#=╥-&="═!]6┬└(╪'╒:└(─0*@!╦╩*─0*@!┌╘)
═╒9$"╚└╞═!═61└╬╪─$]└#[┬43╦2,3\└,@71?╩╚└╩╤└╠┬1└╧└╟╚└"╤└╩╦(╠0*&
═└╚4#╚└╬╤└╧└5..─!─0+╨└╘╙%%:└$╠0(0!<┬╤└═#0╚└╞╤└╚╘&╒:└'╠0*-"=6@
═!╦$"╩╔╩@"+$"├0?53#/_╩0"┬└$╥.%@"╔└*╪'╒8╘'╒:4"╔└..!]488*─└╦@?5
═├0?5╚!┬╤└─├(╠0*╚:(╪'╒1┴@└└└└└(╘\%╞┬-.┴9╚├3╠6:!┴╔└8╘]%╞┴╔└$┬═
═/19(╩0!(╦3╨62(╔(╞$┬═└/](╦0?5╚@".!]6@")$"┬*╘)╒9$"┬+╩*─0*@":╘&
═╒9$"╦3╠62*╘┌%─┴@>"└^%┬"8%─╙1%:4"┴0┬┼└╪4)╩0.@$╪4&┴└<@0!6@└+$(
═┴0+(╠0┬%└┌└:╩<61")└-└&╨└#0└┴┴@>(╘-%╪╨.%╥└^%┬"8%╩└:╩<2@#!┬═
═#1=═)!.-#1>1",┬═#┴=═)1.-#┴>1"*─└*╚╘/%┌─>╚!.%!╚0'┴02$!2└1%╥└└
═%4╙1%0└└└└└@4!>@!+$$╘"╞@#+$$╙203╥+$$[243╚@"╨└>┬.$!>(╦0╘7╘03(
═╦0╪7\02═#╤?═$!>╨╙╩└"╠02╩╥+$$┴@2%!6"@└+$$╩╠┬╤!(8$┴05@╦1╪3╦!\3
═┴0┬$":└,╠0├-)!/╨└6#(╠0├-)1/╨└6"╔'╩└3┴0:$!╥!└%2"[%$╤=%╨└└>(╓+
═%╪╥,%╥└^%┬"8%╩╓+%┌╥,%╪4*┴└╬@└+$*┴0;(╠0╩%!┌└,╔0╩1",┬┼"┘$(╚!2┼
═!╔$(╥*4'─0┬@&╦$&╥<+╨(*╟!─0@8╔09╔#╚4&─└+╞!┌└"╠0:%!,┬╤!╚4%(└└5
═3-$5╩<.1"*└,╠0:%!,┬╤!╚4%($48╔0┬─":8&┴@┬╞!╪8)╚@└8(!╠8(+╠43-$5
═└└└└└(╘9&(╪8&(╨7&└┴╚*0&-&┴┬@![$(┴06@!╦$(&&─"┴02@└[─7&)$$┬!#╪
═8*└"╔0┬1"╠┬┼"9$*╚!>╤"╔$$┬!#┘8'┬-┬╤>,├!>═!]5(╩0"-!]6@$[$"╘!┘╚
═├0?5(#╪6()@6╩<*1"*└,╦8╠7─0├(╦8╨7─0┴,╘16┼└╩0#&&─.─└'(┴0:$!┌└.
═╠0*%",┬╤└╚4)($└5╚└╥╤"(4*╥+$(┴0╬═┬╤>╠├!>%!(0%($48╚!╩╔╨┘$(╔@┬─
═"6┬-!]6*6!┴@└'┬╬!]6.╒!┬┬└(╪'╒84*┴└╬@└╦$*┴0├(╠0╩%":└:╠0├)╨_└+
═╩>&╬╒!┬.!]4╪6&"@%+$(╤0+0[<┬╤",4#╘.:@#+$(┴03(╠0┬%!:└1╠0╩1!*└4
═╠0╩1!,├└&)#╫(+╠4╦═08├@?5&%┴@└*─!├3╚9╩8╬@&:("(/╘3╩=╓@&:(!(/╘3
═╩2.@&╩(!(/╘3╩6>@&╩(!(/╘3╩:╬@&╩(!(/╘3╩>^@&╩(!(/╘3╩3.@&┌(!(/╘3
═╩6┬@&┌(!(/╘33)\;╚┴╬╔└)╘└╒,╚0^╩┼0┴0*%└┌─(├1├4╩0"@"(╘"╒(╨#╒*┼!
═├034╩0"-!=2╔\(╘&╒*4"╔└.-└-2,└=2┼└@4#╘└>╔>*└└(+╘6┘@+0└╬8#[┬#0
═╬─╥┘&2└├%╚╘└#(╨!#*─$╚!╩-!└╥,!0╥╔/*└└(+╘6[╚$%╩0"@#""-%╘╙╨&<┴)
═+"!42$┼3($┼3($1%3$%9(%!23╘-%4╒,@,2└╩#0└@(╤:-&└╥,&0╥╔2╩└:├1╨,
═├!╘,╩7┬@└""]%╬┌"!:─8╚└╨@├1=,-┴╦(22╨@5$┴)4╥!)4╥!$14╤!62!04─]#
═15-3(#(-└"└├%╚╘╨#(╨╤#*╞.╚!╩--└╥,-0╥╔═*└└(+╘6[╚,%╩3"@#""-%╘╤┌
═&╠┴)+"!42$┼3($┼3($1%3$%9(%!23╘-%4╒,@,╨╘└(",6├4@,├$─,╩=*@&╚╒,
═#(╤-#*╟╨╚└└@╧1;╬┴└6╔2*└,((╘73+╪:╥$─╠(%1(25,@25,@1$5,05─@4%)/
═0╘534╥└╘#0└@(╤:-8└╥,80╥╔%╩└;├60,├&4,╩2╥@└2"]%╬┌%!:┼@╚└╨@├1=,
═└┴╧(22╨@5$┴)4╥!)4╥!$14╤!62!04─]#15-3(#4-└"└├%╚╒╪#(╤┘#*┼3╚!╬-
═?└╥,?0╙╬@└6╔>*└,((╘73$8;╥$─╠(%1(25,@25,@0─╤!0─)%4@╘└(",6├9└,
═├)$,╩8┬@&╪╓4#(╥5#.┌└!:╞0╚└╨@├1=,>╤╧(22╨@5$┴)4╥!)4╥!34$┼.3─52
═("╠-└*─└├0?5╩0╪@╘╧^╔╩*└,(%╚8╦:╨,╦*╘,┴8"$@:└└╠8#╨!┬#2_\├0]╩╞╚
(╚└╨@╒1┴,╩1╠└
└
END
-NUCODE-END 1 2258 1430BDC2
========================================================================┼╬─===